Lesson 2 - Build Form Profile App Using Multi-input Form Data

Building Form Profile App

Step-by-Step Procedure to Implement React Form Profile App

1. Setting Up the Project

  1. Create a New React Project: Run the following command to create a new React project:

    npx create-react-app react-formProfile-app
    cd react-formProfile-app
  2. Install Bootstrap CSS: Install Bootstrap using npm:

    npm install bootstrap

    Import Bootstrap in the main.jsx file:

    import 'bootstrap/dist/css/bootstrap.min.css';
  3. Setup Folder Structure: Create a components folder inside the src directory:

    src/
    |-- components/
        |-- FormProfile.jsx
    |-- AppFormProfile.jsx

2. Implement the Parent Component: AppFormProfile.jsx

The parent component manages the state for the form data and profiles, and passes the necessary props to the child component.

Code for AppFormProfile.jsx:

import React, { useState } from 'react';
import FormProfile from './components/FormProfile';
import 'bootstrap/dist/css/bootstrap.min.css';

function AppFormProfile() {
  const [formData, setFormData] = useState({
    userImage: './src/assets/IMG_0003.JPG', // Replace with a real image URL,
    name: '',
    email: '',
    profession: ''
  });

  const [profiles, setProfiles] = useState([]);

  const handleChange = (event) => {
    const { name, value } = event.target;
    setFormData({
      ...formData,  // create a copy of the formData array.
      [name]: value
    }); //update form data input
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    setProfiles([...profiles, formData]);
    setFormData({ userImage: '', name: '', email: '', profession: '' });
  }; //clear form inputs

  return (
    <div className="container mt-5">
      <h1 className="text-center">React Profile App</h1>
      <form onSubmit={handleSubmit} className="mb-4">
        <div className="mb-3">
          <input
            type="text"
            name="userImage"
            value={formData.userImage}
            onChange={handleChange}
            placeholder="Image URL"
            className="form-control"
          />
        </div>
        <div className="mb-3">
          <input
            type="text"
            name="name"
            value={formData.name}
            onChange={handleChange}
            placeholder="Name"
            className="form-control"
          />
        </div>
        <div className="mb-3">
          <input
            type="email"
            name="email"
            value={formData.email}
            onChange={handleChange}
            placeholder="Email"
            className="form-control"
          />
        </div>
        <div className="mb-3">
          <input
            type="text"
            name="profession"
            value={formData.profession}
            onChange={handleChange}
            placeholder="Profession"
            className="form-control"
          />
        </div>
        <button type="submit" className="btn btn-primary">Add Profile</button>
      </form>
      <div className="profile-container d-flex flex-wrap gap-4">
        {profiles.map((profile, index) => (
          <FormProfile
            key={index}
            userImage={profile.userImage}
            name={profile.name}
            email={profile.email}
            profession={profile.profession}
          />
        ))}
      </div>
    </div>
  );
}

export default AppFormProfile;

3. Implement the Child Component: FormProfile.jsx

The child component receives props from the parent and renders the user profile.

Code for FormProfile.jsx:

import React from 'react';
import PropTypes from 'prop-types';

function FormProfile({ userImage, name, email, profession }) {
  return (
    <div className="user-profile d-flex border p-3 rounded">
      <div className="left-item me-3">
        <img src={userImage} alt={name} className="img-fluid rounded" style={{ width: '100px', height: '100px' }} />
      </div>
      <div className="right-item d-flex flex-column">
        <h5>{name}</h5>
        <p>{email}</p>
        <p>{profession}</p>
      </div>
    </div>
  );
}

FormProfile.propTypes = {
  userImage: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  email: PropTypes.string.isRequired,
  profession: PropTypes.string.isRequired,
};

export default FormProfile;

4. Styling with Bootstrap

Leverage Bootstrap classes for styling. Use d-flex, flex-row, and flex-column for layouts. Add custom styles in App.css if necessary.

5. Run the Application

Start the application using the following command:

npm run dev

Final Output

The app will display a form at the top for adding profiles. Below the form, profiles will be displayed in a flexbox layout with images on the left and details on the right.

Summary

This walkthrough demonstrates how to:

  1. Lift state up in the parent component.

  2. Pass props to child components.

  3. Use Bootstrap for responsive styling.

  4. Manage forms and lists dynamically in React.

Challenge : Update the component to fetch image URLs from Unsplash and allow the user to select one.

Code for Updated AppFormProfileUpdated.jsx:

import React, { useState, useEffect } from 'react';
import FormProfileUpdated from './components/FormProfileUpdated.jsx';
const apiKey =''

function AppFormProfileUpdated() {
  const [formData, setFormData] = useState({
    userImage:'https://images.pexels.com/photos/2014422/pexels-photo-2014422.jpeg',
    name: '',
    email: '',
    profession: ''
  });

  const [profiles, setProfiles] = useState([]);
  const [imageOptions, setImageOptions] = useState([]);
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState(null)

  useEffect(() => {
    // Fetch image options from Unsplash
      const fetchImages = async (query) => {
      setIsLoading(true)
      try {
        const response = await fetch(
          `https://api.pexels.com/v1/search?query=${query}&per_page=2`,
          {
            headers: {
              Authorization: apiKey,
            },
          },
        )

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`)
        }

        const data = await response.json()
        setImageOptions(data.photos)
    
        console.log("Photos: ", data.photos)
      } catch (error) {
        setError('Failed to fetch images.')
      } finally {
        setIsLoading(false)
      }
    }

    fetchImages('people')
  }, []);

  const handleChange = (event) => {
    const { name, value } = event.target;
    setFormData({
      ...formData,
      [name]: value
    });
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    setProfiles([...profiles, formData]);
    setFormData({ userImage: '', name: '', email: '', profession: '' });
  };

  return (
    <div className="container mt-5">
      <h1 className="text-center">React Profile App</h1>
      <form onSubmit={handleSubmit} className="mb-4">
        <div className="mb-3">
          <label htmlFor="userImage" className="form-label">Select an Image:</label>
          <select
            id="userImage"
            name="userImage"
            value={formData.userImage}
            onChange={handleChange}
            className="form-select"
          >
            <option value="">Choose an image</option>
            {imageOptions.map((url, index) => (
              <option key={index} value={url}>{url}</option>
            ))}
          </select>
        </div>
        <div className="mb-3">
          <input
            type="text"
            name="name"
            value={formData.name}
            onChange={handleChange}
            placeholder="Name"
            className="form-control"
          />
        </div>
        <div className="mb-3">
          <input
            type="email"
            name="email"
            value={formData.email}
            onChange={handleChange}
            placeholder="Email"
            className="form-control"
          />
        </div>
        <div className="mb-3">
          <input
            type="text"
            name="profession"
            value={formData.profession}
            onChange={handleChange}
            placeholder="Profession"
            className="form-control"
          />
        </div>
        <button type="submit" className="btn btn-primary">Add Profile</button>
      </form>
      <div className="profile-container d-flex flex-wrap gap-4">
        {profiles.map((profile, index) => (
          <FormProfileUpdated
            key={index}
            userImage={profile.userImage}
            name={profile.name}
            email={profile.email}
            profession={profile.profession}
          />
        ))}
      </div>
    </div>
  );
}

export default AppFormProfileUpdated;

3. Implement the Child Component: FormProfile.jsx

The child component receives props from the parent and renders the user profile.

Code for FormProfileUpdated.jsx

import React from 'react';
import PropTypes from 'prop-types';

function FormProfileUpdated({ userImage, name, email, profession }) {
  return (
    <div className="user-profile d-flex border p-3 rounded">
      <div className="left-item me-3">
        <img src={userImage} alt={name} className="img-fluid rounded" style={{ width: '100px', height: '100px' }} />
      </div>
      <div className="right-item d-flex flex-column">
        <h5>{name}</h5>
        <p>{email}</p>
        <p>{profession}</p>
      </div>
    </div>
  );
}

FormProfile.propTypes = {
  userImage: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  email: PropTypes.string.isRequired,
  profession: PropTypes.string.isRequired,
};

export default FormProfileUpdated;

4. Styling with Bootstrap

Leverage Bootstrap classes for styling. Use d-flex, flex-row, and flex-column for layouts. Add custom styles in App.css if necessary.

5. Run the Application

Start the application using the following command:

npm run dev

Final Output

The app will display a form at the top for adding profiles. Below the form, profiles will be displayed in a flexbox layout with images on the left and details on the right.

Challenge : Capture Live Image from PC Camera

I want to modify App.jsx react component to capture the image data from the usb pc camera instead of using image URL.

// App.jsx
import { useState } from 'react'
import './App.css'
import 'bootstrap/dist/css/bootstrap.css'
import FormProfile from './components/FormProfile.jsx'


function App() {
  const [formData, setFormData] = useState({
    userImage: './src/assets/IMG_0003.JPG', // Replace with a real image URL,
    name: '',
    email: '',
    profession: '',
  })

  const [profiles, setProfiles] = useState([])

  const handleChange = (event) => {
    const { name, value } = event.target
    setFormData({
      ...formData,
      [name]: value,
    }) //update form data input
  }

  const handleSubmit = (event) => {
    event.preventDefault()
    setProfiles([...profiles, formData])
    setFormData({ userImage: '', name: '', email: '', profession: '' })//clear form inputs
  }

  return (
    <>
      <h1>Form Profile App</h1>
      <div className="container mt-5 col col-md-8">
        <form onSubmit={handleSubmit} className="mb-4">
          <div className="mb-3">
            <input
              type="text"
              name="userImage"
              value={formData.userImage}
              onChange={handleChange}
              placeholder="Enter image url .e.g.,  https://unsplash.com/t/people"
              className="form-control"
              required="true"
            />
          </div>
          <div className="mb-3">
            <input
              type="text"
              name="name"
              value={formData.name}
              onChange={handleChange}
              placeholder="Name"
              className="form-control"
              required="true"
            />
          </div>
          <div className="mb-3">
            <input
              type="email"
              name="email"
              value={formData.email}
              onChange={handleChange}
              placeholder="Email"
              className="form-control"
              required="true"
            />
          </div>
          <div className="mb-3">
            <input
              type="text"
              name="profession"
              value={formData.profession}
              onChange={handleChange}
              placeholder="Profession"
              className="form-control"
              required="true"
            />
          </div>
          <button type="submit" className="btn btn-primary">
            Add Profile
          </button>
        </form>
        <div className="profile-container d-flex flex-wrap gap-4 ">
          {profiles.map((profile, index) => (
            <FormProfile
              key={index}
              userImage={profile.userImage}
              name={profile.name}
              email={profile.email}
              profession={profile.profession}
            />
          ))}
        </div>
      </div>
    </>
  )
}

export default App

Result

Summary

This walkthrough demonstrates how to:

  1. Lift state up in the parent component.

  2. Pass props to child components.

  3. Use Bootstrap for responsive styling.

  4. Dynamically fetch and display image options from an external API.

  5. Manage forms and lists dynamically in React.

Last updated