Sergey Khmelevskoy
Sergey Khmelevskoy

Reputation: 2544

React hooks useState doesnt update correctly

I have a weird issue with react + mobx + hooks API not updating state correctly

This is a console output of the example below. Nevermind about findDomNode error, it's related to toast component library being out of date

enter image description here

Let's say there is a list of services and an array of corresponding ids. It's an admin panel where I want to add or delete current services on the server-side. I store selected services (i.e. checkboxes) in useState. It's also possible to remove service from the backend and therefore I need to update the selected (checkboxed) list on the frontend with what comes in a backend response

The problem is that hook showing that update is right inside useEffect, but inside render function (handleListColCallback) selected values is not updating, always keeping deleted values

Have tried multiple combinations of useMemo, useCallback, etc. There result is always the same. Am I doing something wrong?

const AdminServices = observer(() => {
  const [selected, setSelected] = useState([]);

  const serviceContext = useContext(ServiceStoreContext);

  const handleListColCallback = async (params) => {
    if (params.type === 'select-checkbox') {
      // STEP 3 - I want to manipulate with state removing or adding checked id to array 
      // ERROR here - selected state still showing old value (not the same as in useEffect)
      console.log('checkbox select', selected);

    } else if (params.type === 'delete-from-node') {
      // STEP 1 - I call delete action, it runs successfully
      await serviceContext
        .deleteFromNode({ service_id: params.id })
    }
  };

  useEffect(() => {
    // STEP 2 - when mobx store updates, I want to reset component state with new values
    setSelected(serviceContext.nodeListArrayIds);
  }, [serviceContext.nodeListArrayIds]);

  useEffect(() => {
    // selected shows current values (this effect is just for testing)
    console.log('selected updated', selected);
  }, [selected]);
}

UPDATE

Issue solved by using following setState and handleListColCallback update. Would be happy if somebody could explain the difference why pure setState is that different vs setState((curState) => ... )

  useEffect(() => {
    setSelected(() => {
      return [...serviceContext.nodeListArrayIds];
    });
  }, [serviceContext.nodeListArrayIds]);

    } else if (params.type === 'select-checkbox') {
      setSelected((selected) => {
        const index = selected.indexOf(params.id);

        if (index === -1) {
          return [...selected, ...[params.id]];
        } else {
          return selected.filter((x) => x !== params.id);
        }
      });

Upvotes: 1

Views: 715

Answers (1)

Nadia Chibrikova
Nadia Chibrikova

Reputation: 5036

It appears that the array in the context (serviceContext.nodeListArrayIds) somehow gets the deleted item re-added (something the OP might want to investigate).

The difference between setSelected(serviceContext.nodeListArrayIds); and setSelected([...serviceContext.nodeListArrayIds]); is that in the first case selected is a reference to serviceContext.nodeListArrayIds and thus will reflect any changes to it. [...serviceContext.nodeListArrayIds] creates a shallow clone (similar to .slice()), so changes to serviceContext.nodeListArrayIds do not affect it.

Upvotes: 1

Related Questions