Home > Blockchain >  Abnormal behavior of useState hook in react
Abnormal behavior of useState hook in react

Time:04-11

I am having issues with my useState hook in react version 17.0.2

Code explanation: Basically, I am displaying numbers of selected group in other block, block of numbers to be displayed. So the function below is called whenever there is change in group selection.

So every time new fresh array of selected group is sent in args of the function. For which I am trying to reset the hook setNumbersToDisplay([]), but it is not working. Instead, it keeps the last values and added new values to it

// Gets called when every group is selected or de-selected
    const selectedGroupsHandler = (groups) => {
        console.log("Groups Selected:")
        console.log(groups)
        console.log('Current numbers in store:')
        setNumbersToDisplay([]) // Reseting value of numbers to display
        console.log('Numbers after refresh:')
        console.log(numbersToDisplay)

        // Adding numbers of seleted groups to numbers to display
        if(groups.length > 0) {
            groups.forEach(selectedGroup => {
                groupsData.forEach(group => {
                    if(selectedGroup === group.groupID) {
                        setNumbersToDisplay(
                            numbersToDisplay.concat(group.contacts)
                        )
                    }
                })
            })
        }
    }
    console.log("Final output for numbers to be displayed:")
    console.log(numbersToDisplay)

Code output after selecting one group: Code output after selecting one group

Code output after selecting other group making it 2: Code output after selecting other group making it 2

Code output after de-selecting one group: Code output after de-selecting one group

Notice how the resetting code for hook setNumbersToDisplay([]) is not working. And added new selected group contacts to last record making the total 6 instead of 2

CodePudding user response:

Use functional state updates so the state updates enqueued in the loops correctly update from the previous state and not the state closed over in callback scope.

Example:

// Gets called when every group is selected or de-selected
const selectedGroupsHandler = (groups) => {
  console.log("Groups Selected:", { groups });
  console.log('Current numbers in store:', { numbersToDisplay });
  setNumbersToDisplay([]); // Reseting value of numbers to display

  // Adding numbers of selected groups to numbers to display
  groups.forEach(selectedGroup => {
    groupsData.forEach(group => {
      if (selectedGroup === group.groupID) {
        setNumbersToDisplay(
          // function update recieves previous state value to update from
          numbersToDisplay => numbersToDisplay.concat(group.contacts)
        );
      }
    });
  });
}

You can't log state right after enqueueing an update, use an useEffect hook to log it after it's updated.

useEffect(() => {
  console.log("Final output for numbers to be displayed:");
  console.log(numbersToDisplay);
}, [numbersToDisplay]);

CodePudding user response:

Essentially your function will be recreated on each new component render which is triggered by state or props changing.

To prevent this you can make use of memoization by wrapping the function in a useCallback as follows.

  const selectedGroupsHandler = React.useCallback((groups) => {
    console.log("Groups Selected:")
    console.log(groups)
    console.log('Current numbers in store:')
    setNumbersToDisplay([]) // Reseting value of numbers to display
    console.log('Numbers after refresh:')
    console.log(numbersToDisplay)

    // Adding numbers of seleted groups to numbers to display
    if(groups.length > 0) {
      groups.forEach(selectedGroup => {
        groupsData.forEach(group => {
          if(selectedGroup === group.groupID) {
            setNumbersToDisplay(
              numbersToDisplay.concat(group.contacts)
            )
          }
        })
      })
    }
    // this array below is the dependency array
  }, [])

This will cache the function. If you need the function to be dependent on any state or prop values you can add those values to the dependency array.

Regarding the logs for the final array you better only log that upon component mounting, then you can log it every time the numbersToDisplay value is updated by adding that to the useEffect dependency array as follows:

React.useEffect(() => {
  console.log("Final output for numbers to be displayed:");
  console.log(numbersToDisplay);
}, [numbersToDisplay]);
  • Related