Antonio Pavicevac-Ortiz
Antonio Pavicevac-Ortiz

Reputation: 7729

React useRef hook: useRef value not be used by a useState function

Before I asked the question I naturally did a search and found this great post:

I'll show my implementation in a second after I explain a little preamble.

Essentially my problem is the updater function in useState is not updating in sync, it is one behind in the data it gets.

The useState function is passed a object which contains and id, lat and lang.

currentCoords: [{id: 1, lat: 40.74003514370453, lng: -73.76452126968704}]
markers: [{id: 0, lat: 40.74168820148129, lng: -73.76539693565009},…]
0: {id: 0, lat: 40.74168820148129, lng: -73.76539693565009}
1: {id: 1, lat: 40.74003514370453, lng: -73.76452126968704}

currentCoords, should mirror the markers array. As you can see it is "setting" the second.

So I know both because setState and useState are asynchronous, you can't rely on the state being updated.

Now this is what I tried:

 useEffect(() => {
    if (userMarkers.length === 1) {
      userMarkersRef.current = userMarkers[0]; // {id: 0, lat: 40.74168820148129, lng: -73.76539693565009}
      setUserCoords(userMarkersRef.current);

      setIsRoutingVisibileFalse();
    }
    if (userMarkers.length === 2) {
      userMarkersRef.current = userMarkers[1]; // {id: 1, lat: 40.74003514370453, lng: -73.76452126968704}
      setUserCoords(userMarkersRef.current);
    }
  }, [JSON.stringify(userMarkers)]);

I have included in the useEffect dependency an array which has been stringified to kick off the hook when it (useMarkers) changes (not to worry that array never goes beyond two items!)

I figured by saving an explicit reference, and then pass it to the updater that would work, but it doesn't :(

This is the setState function, which is saving it to local storage:

  setUserCoords: coords => {
   setUser({
     ...user,
     currentCoords: [{ ...user.currentCoords[coords.id], ...coords }]
   });
  },

Upvotes: 1

Views: 265

Answers (1)

Anchor
Anchor

Reputation: 1371

Essentially my problem is the updater function in useState is not updating in sync, it is one behind in the data it gets.

It looks like whichever user is being referred to via closure inside of setUser is stale.

Since your next state depends on your previous state, you can use the other form of setState to get the most up-to-date snapshot of the previous state.

i.e -

setUserCoords: coords => {
   setUser((prevState) => {
     return {
       ...prevState.user,
       currentCoords: [{ ...prevState.user.currentCoords[coords.id], ...coords }]
     }
   });
  },

From the React docs:

Both state and props received by the updater function are guaranteed to be up-to-date. The output of the updater is shallowly merged with state.

Upvotes: 1

Related Questions