Search code examples
reactjsreact-hooksuse-state

Old values for React state while passing it to a function


In my simple app I have two (styled-component's) inputs with type of date:

<DateInput 
  type="date" 
  name="startDate" 
  value={state.startDate} 
  onChange={(e) => handleInputChange(e)} 
  required 
/>
<DateInput 
  type="date" 
  name="endDate" 
  value={state.endDate} 
  onChange={(e) => handleInputChange(e)} 
  required 
/>

Every time I change the date of any of those inputs the function handleInputChange is being called:

  const handleInputChange = (e) => {
    setState({ ...state, [e.target.name]: e.target.value });
    dataValidation(state);
  };

This function updates the state with the new values from date inputs and then I'm running my dataValidation function where I'm passing my state to validate the dates (I'm checking if the start date is not greater than the end date). My problem is that when I pass this state it has older values, for example:

Input value: 01.01.2021
State value: 12.31.2019

Input value: 01.02.2021
State value: 01.01.2021

Input value 01.03.2021
State value: 01.02.2021

By that, I can't compare my dates. I see that this is some kind of latency for state updates. Can I somehow wait for my dataValidation function to run after the state will be updated?


Solution

  • State is updated asynchronously and the state is constant within a particular render of a react component. To see the updated state, component needs to re-render.

    You can fix your problem using one of the following options:

    • Use the useEffect hook that executes whenever your state is updated and the component has re-rendered. You can call the dataValidation function from the useEffect hook but then you need to add it in the dependency array.

      useEffect(() => {
        dataValidation(state);
      }, [state, dataValidation]);
      

      (Make sure you wrap the dataValidation function in the useCallback hook to avoid running the useEffect hook infinitely).

      Alternatively, you could move the code of the dataValidation function inside the useEffect hook.

      useEffect(() => {
        // write your data validation code here
      }, [state]);
      
    • If you don't want to use the useEffect hook, you could change the implementation of the handleInputChange function as shown below:

      const handleInputChange = (e) => {
         // create an object representing the new state
         const newState = { ...state, [e.target.name]: e.target.value };
      
         // pass the "newState" object to both "setState" and
         // "dataValidation" function
         setState(newState);
         dataValidation(newState);
      };