Project 74 of 100 - Multipart Form
Tags: javascript, react, 100daysofcode
Published: 2021-06-02 12:37:05

*Hey! I'm on a mission to make 100 React.js projects. Please follow my dev.to profile or my [twitter](https://www.twitter.com/jwhubert91) for updates and feel free to reach out if you have questions. Thanks for your support!*

Link to today's deployed app: [Link](https://100-react-projects-day-74-multipart-form.netlify.app/)
Link to the repo: [github](https://github.com/jwhubert91/100daysofreact/tree/master/day-74-multipart-form)

I was looking for a short project a week ago to continue my 100 projects list and came across a 20 minute Youtube tutorial that was mailed out as a part of the weekly FreeCodeCamp.org email [link here](https://www.youtube.com/watch?v=dybbUOmtd0Y&feature=youtu.be). Of course buyer beware, anyone who promises you'll implement a feature in a short Youtube tutorial probably isn't helping build the entire project, and the video ends mostly handling data with pseudocode.

So last weekend I tried to implement this with the Context API, and then found it was so simple it wasn't even worth using Context, and instead just kept state and app-wide functions in the `App` component. Of course a real application wouldn't function so simply.

##Basic Structure
The basic structure of the application places all importance on the `App` component. It didn't have to be App, it could have been a page component several layers deep, but the point is it's a parent component and the multiple steps in the form are handled simply as child components one level down.

Here is the JSX:

```JS
import React, {useState} from 'react'

function App() {
...
  return (
    <div className="App">
      <h1>
        React multi-part form
      </h1>
      <p>
        by <a href="https://twitter.com/jwhubert91">@jwhubert91</a>
      </p>
      <main className='main-form'>
        <FormPage page={page} data={data} setData={setData} />
        <button onClick={handleButtonClick}>{buttonText()}</button>
      </main>
    </div>
  );
}

export default App;
```

In the App component I wanted to share a button between all pages so I kept it here in the parent component.

The child component was so simple that I knew what props I needed to pass down even before creating it. It would be a simple div where some logic would render a different input to the user depending on the page. I then pass down the data state variable and the setData handler so that we can build controlled inputs in the child components using state from the parent component.

Then I added state variables and handlers to the `App` component, above the JSX:

```JS
function App() {
  const [page,setPage] = useState(0)
  const [data,setData] = useState({
    email: '',
    username: '',
    color: '',
    spam: true
  })

  const handleButtonClick = () => {
    if (page === 4) {
      setPage(0)
    } else {
      setPage(prevPage => prevPage + 1)
    }
  }

  const buttonText = () => {
    if (page < 3) {
      return "next"
    } else if (page === 3) {
      return "submit"
    } else {
      return "edit"
    }
  }
return (...)
}
```

Here we simply handle the button text and the page number as the user clicks through the form.

##Child Form Component
Now, in the child component we need to render different code depending on what page is being passed through to props. Here I just extended what the method that was taught in the otherwise not very useful Youtube video above. We use conditional rendering by [short circuiting](https://codeburst.io/javascript-short-circuit-conditionals-bbc13ac3e9eb) the &&. 

We can simply do this multiple times in a row and render whatever is called for by the page number. Pretty simple, I know, but it works well for this use case. 

```JS
import React from 'react'

function FormPage({page,data,setData}) {
  return (
    <div>
      {page === 0 && 
        <input 
          type="text" 
          placeholder="Email"
          value={data.email}
          onChange={(e) => setData(prevData => ({
            ...prevData,
            email: e.target.value
          }))}
        />}
      {page === 1 && 
        <input 
          type="text" 
          placeholder="Username"
          value={data.username}
          onChange={(e) => setData(prevData => ({
            ...prevData,
            username: e.target.value
          }))}
        />}
      {page === 2 && 
        <input 
          type="text" 
          placeholder="Favorite color"
          value={data.color}
          onChange={(e) => setData(prevData => ({
            ...prevData,
            color: e.target.value
          }))}
        />}
      {page === 3 && 
        <div>
          <p>Do you want spam?
            <input 
                type="checkbox" 
                checked={data.spam}
                onChange={(e) => setData(prevData => ({
                  ...prevData,
                  spam: e.target.checked
                }))}
            />
          </p>
        </div>
      }
      {page === 4 && 
        <div className="results">
          <p><strong>email:</strong> {data.email}</p>
          <p><strong>username:</strong> {data.username}</p>
          <p><strong>favorite color:</strong> {data.color}</p>
          <p><strong>spam me?</strong> {data.spam ? "Yes" : "No"}</p>
        </div>
      }
    </div>
  )
}

export default FormPage
```

For the last page, we simply render all of the user data like in a User Profile screen and ask the user if they want to edit, which takes them back to page 0.

That's it! Do you have a favorite way to implement multi-step forms, maybe using Context or Redux? Or even saving to the back-end with each step? Drop a comment below :)

**If you like projects like this and want to stay up to date with more, check out my Twitter [@jwhubert91](https://twitter.com/jwhubert91), I follow back! See you tomorrow for another project.**