Home > Blockchain >  INPUT MANAGEMENT IN REACT FORM
INPUT MANAGEMENT IN REACT FORM

Time:07-18

i'm coding a dynamic react form. I want to be able to print a field value, update the state according to it's value changes, and all of that while i'm typing inside that field. I also want to print the state in the console by clicking on the submit button. Not that hard, i know, and i did it (almost perfect). Here is how i did :

  • i declared and initial state, with inside and object called "inputs", with 3 attributes, name, email and password.
  • i declared my reducer with one switch case, with the action type "SETINPUTS", that returns the state and its inputs object set to the action payload.
  • i declared a constant "inputs" assigned to a useState with initial value set to the initial state.inputs.
  • i declared a "state" constant assigned to a useReducer initialized with my initial state.
  • then, i declared two functions : handleChange() that update "inputs"(useState const) and dispatch the "SETINPUTS" action with a payload equal to "inputs"; handleSubmit() that prevent the default event of submitting and print the state in the console.
  • and, at last, for each field, i set the value attribute to the corresponding "inputs" value and the onChange to the handleChange() function.

here is my code itself :

import React, { useReducer, useState } from 'react'
import './Form.css'

const initialState = {
  inputs : {
    name: "",
    email: "",
    password: ""
  }
};
const reducer = (state, action) => {
  switch(action.type){
    case 'SETINPUTS' :
      return { ...state, inputs: action.payload };
    default: 
      return state;
  }
}

const Form = () => {

  const [inputs, setInputs] = useState(initialState.inputs)

  const [state, dispatch] = useReducer(reducer, initialState)
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(state)
  } 

  function handleChange(e){
    setInputs({ ...inputs, [e.target.name]: e.target.value });
    dispatch({ type: 'SETINPUTS', payload: inputs })
  }

  return (
    <div className='form'>
      <form onSubmit={handleSubmit}>
        <label htmlFor="name">Name</label>
        <input 
          type="text" 
          name='name' 
          placeholder='John Doe'
          value={inputs.name}
          onChange={handleChange}
        />
        <label htmlFor="email">Email</label>
        <input 
          type="email" 
          name='email' 
          placeholder='[email protected]' 
          value={inputs.email}
          onChange={handleChange}
        />
        <label htmlFor="password">Password</label>
        <input 
          type="password" 
          name='password' 
          placeholder='enter your password please'
          value={inputs.password}
          onChange={handleChange}
        />
        <button type='submit'>Submit</button>
      </form>
      <div>
        {state.inputs.name} {state.inputs.email} {state.inputs.password}
      </div>
    </div>
  )
}

export default Form

The problem, as you can see on this picture, is that what i type is not exactly what is returned or print in the console. i have to delete the last letter, to add a space for the last letter to be taken into account. When i delete the last character, it remains, etc ... It's quite strange, i need your help please.

THANKS A LOT :) !

CodePudding user response:

The Issue is with your handleChange method. Setting state is a async method... so you will not get the latest value in state. Try below implementation:

function handleChange(e) {
    setInputs({ ...inputs, [e.target.name]: e.target.value });
  }

  useEffect(() => {
    dispatch({ type: "SETINPUTS", payload: inputs });
  }, [inputs]);

or this one:

function handleChange(e) {
    setInputs({ ...inputs, [e.target.name]: e.target.value });
    dispatch({
      type: "SETINPUTS",
      payload: {
        ...inputs,
        [e.target.name]: e.target.value
      }
    });
  }

CodePudding user response:

In your handleChange function, setInputs is an async operation, meaning the dispatch call will not be using the latest state of inputs.

You could instead pass in e.target.value directly:

function handleChange(e){
    setInputs({ ...inputs, [e.target.name]: e.target.value });
    dispatch({ type: 'SETINPUTS', payload: {...inputs, [e.target.name]: e.target.value }});
}

However, you may want to rethink your design to depend either on the component's state, or the reducer store only, rather than having both and trying to keep them in sync.

  • Related