Dynamic Forms Made Easy with React UseState

Dynamic Forms Made Easy with React UseState

Welcome back 馃コ馃コ, nice to have you back. So this article is the first series of the hooks articles meaning in my subsequent articles I will be diving into other hooks but for now, let's refresh our memory on useState

Prerequisite

  • conditional rendering(Check here)

  • functional components

  • Switch Statement

  • array Destructuring

  • useState

  • Props(you can check out my article on props)

  • You

// useState Sample
const [learn, setLearn] = useState("learn hooks")

Why use States

States is a built-in react object that contains data or information about our component,it is dynamic meaning it can change anytime.

Having states in our component helps us to track changes in our app and re-renders the particular section that changed rather than rendering the whole app completely. It gives our app a form of interactivity.

Using the react hook:- useState helps us preserve our initial states in between rendering to updated states so we can easily switch to either of the states.

useState

useState returns two things an initial value and a **function **that updates the initial value. Having noted this we could use array Destructuring to assign variables to the returning object.

In our previous sample :

  • learn variable is assigned the learn hooks value which is the initial state (let learn = "learn hooks")

  • setLearn is assigned the function that updates the variable learn.

Let's take a look at an example

import React,{useState} from "react";

export default Learn = () => {
  const [user, setUser] = useState("")

  function newUser(e){
    setUser(e.target.value)
    console.log(user)
  }
  return (
    <div className="App">
      <label>Enter your name.</label>
      <input type = "text"
        value = {user}
        placeholder ="my name is"
        onChange = {newUser}/>
    </div>
  );
}

Explaining what we did here

  • assigned user a state of empty string

  • created a function newUser that listens to an event then returns the value (in our case it's listening to the input event)

  • updated our user state with the value of the event ie. setUser(e.target.value).

  • in our input field assigned the user state to value variable

  • onchanging the input field,the newUser function is called

From here we can always get our initial value and updated values too. Whoops 馃き we have had that out of the way let's dive into the juicy part馃ぃ馃コ

Demo

Demo gif.gif

Pseudocode

  • we have three stages(seperation of concern ie. having those stages as it's own component)

  • for each stage their is a response from the user

  • and each stage depends on the previous response to render.

  • in our app.js we can define those stages and states then using switch statement we can now render the different stages.

  • on defining the states ,we could pass down the states as props to our different stages.

  • within the stages we will use conditional rendering to render different things based on the user input(states).

Redux is a preferred way of doing state management in react but for the sake of understanding the basics we would be using this approach.

Screenshot (2).png

here we have created our different stages where App.js serves as our parent component and the different stages our child components.

Parent Component(App.js)

  1. importing the components and setting states for the different child components

    • we had to use the useEffect ( see Effect hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined)

will discuss more on useEffect hook in my next article

import React,{useState,useEffect}from 'react'
import Stage0 from "./Stage0.js"
import Stage1 from "./Stage1.js"
import Stage2 from "./Stage2.js"


const App = () => {
  //the form state handles the different stages on switch
    const[form,setForm] =useState(0)
    // stage 0
    const[response,setResponse] = useState("")
    //stage 1
    const[response1,setResponse1] = useState("")
    // stage 2
    const[response2,setResponse2] = useState("")

// usually we will have a callback function inside useEffect which runs
// whenever there is a re-render,but if you include the form state as the dependency.
// the function will then run only when there is an update in the form state.

useEffect(() => 
{
// code goes here
}, [form])

On return

  • we had to use the immediate invoked function (IIFE) instead of function declaration because we want the switches running once in our app and because of the JSX restriction, check out here for more details on IIFE.

  • applied the switch statement

  • and then passed down the states to the various stage components

return(
      <div style={{textAlign:"center"}}>
      <h1> Learning Hooks in react</h1>
      <p> rendering different forms based on users input.</p>

      {/* the form switches */}
      {/* using immediate invoked function IIFE  */}

      {(() =>{
        switch(form)
        {
         default:
         case 0:
          return(
          <Stage0
          form={form} 
          setForm={setForm} 
          response={response} 
          setResponse={setResponse}/>
          )

          case  1:
          return(
          <Stage1
          form={form} 
          setForm={setForm} 
          response={response} 
          response1={response1} 
          setResponse1={setResponse1}/>
          )

          case  2:
          return(
          <Stage2
          form={form} 
          setForm={setForm} 
          response1={response1} 
          response2={response2}   
          setResponse2={setResponse2}
          />
          )
        }
      })()}
      </div>
    )}

final look of our App.js

import React,{useState,useEffect}from 'react'
import Stage0 from "./Stage0.js"
import Stage1 from "./Stage1.js"
import Stage2 from "./Stage2.js"


const App = () => {
  //the form state handles the different stages on switch
    const[form,setForm] =useState(0)
    // stage 0
    const[response,setResponse] = useState("")
    //stage 1
    const[response1,setResponse1] = useState("")
    // stage 2
    const[response2,setResponse2] = useState("")

// usually we would put a function inside useEffect to trigger 
// whenever a specified thing changes,but if you leave the function empty it 
// will just re-render the component or state
// here it is re-rendering the form state

useEffect(() => {}, [form])

    return(
      <div style={{textAlign:"center"}}>
      <h1> Learning Hooks in react</h1>
      <p> rendering different forms based on users input.</p>

      {/* the form switches */}
      {/* using immediate invoked function IIFE  */}

      {(() =>{
        switch(form)
        {
         default:
         case 0:
          return(
          <Stage0
          form={form} 
          setForm={setForm} 
          response={response} 
          setResponse={setResponse}/>
          )

          case  1:
          return(
          <Stage1
          form={form} 
          setForm={setForm} 
          response={response} 
          response1={response1} 
          setResponse1={setResponse1}/>
          )

          case  2:
          return(
          <Stage2
          form={form} 
          setForm={setForm} 
          response1={response1} 
          response2={response2}   
          setResponse2={setResponse2}
          />
          )
        }
      })()}
      </div>
    )} 

  export default App;

The output

Screenshot (5).png

Stage 0(child component)

  1. Passing the state as arguement in our function.
import React from 'react'

const Stage0 = ({form,setForm,response,setResponse}) => 
{

  return (
     <div>
     </div>
       )
}
 export default Stage0

On return

  • we set the value of our input to the response state which is an empty string.

  • on clicking we update the response state with the picked option which can be graduate or student.

  • added buttons to display different forms

  • which on clicking the next button the next form is displayed and on clicking the previous button the the previous form is displayed.

return (
    <div >
    <label>1.  Are you a Graduate or a Student?</label>
    <div style={{marginTop:"20px"}} >
    <input 
     type='radio'
     name='response' 
     value={response}
     onClick ={()=>setResponse("graduate")}
     />
     <label>Graduate</label><br/>


     <input 
     type='radio'
     name='response' 
     value={response}
     onClick ={()=>setResponse("Student")}
     />
     <label>Student</label>


   </div>
   {/* adding buttons to display different forms */}
   <div style={{marginTop:"40px"}}>
    <button onClick={()=>setForm(form - 1)}>Previous</button>
    <button onClick={()=>setForm(form + 1)}>Next</button>
   </div>

    </div>
  )
}

but their is something missing? with our recent code the user can get to move to the next form without picking an option but that's not what we want. so to make sure an option is chosen before they can go to the next form, we need to set a state that handles that.

The Function

  • we created a function that handles the state.

  • if their is a response from the user, on clicking next move to the next form

  • else display an error message.

  • then we call the function in our next button.

const [error,setError] = useState("")

function handleNext (){
        if(response){
            setForm(form + 1);
        }
        else{
            setError("每ou have to pick an option")
        }
    }

// the conditional rendering
{error && (
            <p style={{marginTop:"15px", textAlign:"center", 
            color:"gray", border:"1px solid #afba15"}}
            >
           {error}
            </p>
         )}

The Final code for Stage 0

import React,{useState} from 'react'


const Stage0 = ({form,setForm,response,setResponse}) => 
{
  // setting error state to check for errors in our component
   const[error,setError] =useState("")

    function handleNext (){
        if(response){
            setForm(form + 1);
        }
        else{
            setError("每ou have to pick an option")
        }
    }
  return (
    <div>
    <label>1.  Are you a Graduate or a Student?</label>
    <div style={{marginTop:"40px"}}>
    <input 
     type='radio'
     name='response' 
     value={response}
     onClick ={()=>setResponse("graduate")}
     />
     <label >Graduate</label><br/>


     <input 
     type='radio'
     name='response' 
     value={response}
     onClick ={()=>setResponse("Student")}
     />
     <label>Student</label>

     {error && (
            <p 
            style={{marginTop:"15px",padding:"10px", 
            textAlign:"center",color:"gray",
            border:"1px solid #afba15"}}>

            {error}
            </p>
         )}
 </div>
   <div style={{marginTop:"40px"}}>
    <button onClick={()=>setForm(form - 1)}>Previous</button>
    <button onClick={handleNext}>Next</button>
   </div>

    </div>
  )
}

export default Stage0;

The Output

Screenshot (7).png

Screenshot (10).png

Stage 1(child component)

The process

  • did similar thing we did in stage 0 where we set the value of the input to the response1 state.

  • on clicking the option we update the state value to the chosen response.

  • using conditional rendering (condition ? expression1 : expression2) we display the particular form we want to display.

import React,{useState} from 'react'


const Stage1 = ({response,setResponse1,form,setForm,response1}) => {
    const[error,setError] =useState("")

    function handleNext (){
        if(response1){
            setForm(form + 1);
        }
        else{
            setError("每ou have to pick an option")
        }
    }

  return (
    <>
    {response === "Student" ? (
    <div style={{marginTop:"40px"}}>

    <label>2. What level are you currently in ?</label>

    <div  style={{marginTop:"20px"}}>
    <input 
     type='radio'
     name='response' 
     value={response1}
     onClick ={()=>setResponse1("100")}
     />
     <label>100</label><br/>

   <input 
     type='radio'
     value={response1}
     name='response' 
     onClick ={()=>setResponse1("200")}
     />
     <label>200</label><br/>

     <input 
     type='radio'
     value={response1}
     name='response' 
     onClick ={()=>setResponse1("300")}
     />
     <label>300</label><br/>

     <input 
     type='radio'
     value={response1}
     name='response' 
     onClick ={()=>setResponse1("400")}
     />
     <label>400</label><br/>


     <input 
     type='radio'
     value={response1}
     name='response' 
     onClick ={()=>setResponse1("500")}
     />
     <label>500</label>


     {error && (
            <p style={{marginTop:"15px",textAlign:"center",
            color:"gray",border:"1px solid #afba15"}}
            >{error}</p>
         )}
</div>
  </div>  
    )
  • still on the conditional rendering, if the initial response is not student but graduate we display a different question.
 :(
    <div>
    <label>2. How was your Undergraduate Studies? </label>
    <div style={{marginTop:"40px"}}>
    <input 
     type='radio'
     name='response' 
     value={response1}
     onClick ={()=>setResponse1("Excellent")}
     />
     <label>Excellent</label><br/>

   <input 
     type='radio'
     name='response' 
     value={response1}
     onClick ={()=>setResponse1("Good")}
     />
     <label>Good</label><br/>

     <input 
     type='radio'
     name='response' 
     value={response1}
     onClick ={()=>setResponse1("Poor")}
     />
     <label>Poor</label><br/>



     {error && (
            <p style={{marginTop:"15px",padding:"10px",
            textAlign:"center",color:"gray",
            border:"1px solid #afba15" }}
            >{error}
            </p>
         )}
</div>
  </div>
  )}

  <div style={{marginTop:"40px"}}>
    <button onClick={()=>setForm(form - 1)}>Previous</button>
    <button onClick={handleNext}>Next</button>
   </div>
</>

  )
}

export default Stage1

The output for stage 1

Screenshot (13).png

Screenshot (15).png

Stage 2 (child component)

  • using the response we get in stage 1 (state response 1),we then use switch statements to render different text.

  • we had to use IIFE to be able to use the switch statement in the return statement.Read More

React allows us to write javascript inside our JSX code (which at first glance looks a lot like HTML) using curly brackets. However there are restriction to this which are the for and the switch statement. That is why we cannot use them in our return statement.

  • for this reason we use the switches outside the return statement and then reference it inside our jsx code in the return statement.

  • Notwithstanding the restrictions, we could still have our switch statement inside our return statement with the help of IIFE

The Final code for stage 2

import React from 'react'

const Stage2 = ({response1,form,setForm}) => {

return(
    <div style = {{marginTop :"40px"}}>
  {
    ( ()=> {
      switch(response1){
      case "100" : 
      return <p>Brace up! with determination you will excel. </p>

      case "500":
      return <p> Congratulations! you are almost done. </p>

      case "Excellent" :
      return <p> You Rock. </p>

      case "Poor":
      return <p>Oh no!! sorry for that experience. </p>

      case "void":
      return <p>Keep Pushing!! </p>

    }})()
  }
<button onClick={()=>setForm(form - 1)}>Previous</button>
</div>  

)}

export default Stage2

Summary

Managing states in different components can get confusing especially when there are multiple child components but i do hope this article clears your confusion. We have finally set up our codes for solving what we wanted, Congrats for making it through the end馃拑馃拑 For more questions or clarifications do use the comment section 鈽猴笍鈽猴笍.