Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
371 views
in Technique[技术] by (71.8m points)

react native - setState hook not updating - though prevState shows correct value

I'm using the state variable to keep track of my previous state.

The animation works (without index), but when I do

const [index, setIndex] = useState(0)

useEffect(() => {
        animation.addListener((scroll) => {
            setTimeout(function() {
                let position = Math.floor(scroll.value / cardWidth + 0.75);
                if (position >= restaurants.length - 1) position = restaurants.length - 1;
                if (position <= 0) position = 0;

                console.log("position: ", position, "index: ", index)

                if (index !== position) {
                    setIndex(prevState => {
                        console.log("previous: ", prevState, "position: ", position, "index: ", index)
                        return position
                    });
                    const { coordinate } = restaurants[position];
                    mapRef.current.animateCamera({
                        center: {
                            latitude: coordinate.latitude,
                            longitude: coordinate.longitude,
                        },
                         altitude: 2000, // zooms in when viewing specific location
                    }, {duration: 1000}) // not sure if duration is actually doing anything
                }
            }, 3000);
        });
        return function cleanUp(): void { animation.removeAllListeners(); }
    }, []);

The index never updates. I've tried checking the prevState inside the setState function and that updates, but if I print index outside of setIndex -- it doesn't update. How can I fix this? EDIT: Found the problem. The function never stops as it is always listening. How can I refactor my code to end the function so the state is changed?

Output from console

position:  1 index:  0
previous:  1 position:  1 index:  0
position:  1 index:  0
previous:  1 position:  1 index:  0
position:  1 index:  0
previous:  1 position:  1 index:  0
position:  1 index:  0
previous:  2 position:  2 index:  0
position:  2 index:  0
previous:  2 position:  2 index:  0
position:  2 index:  0
previous:  3 position:  3 index:  0
position:  3 index:  0
previous:  3 position:  3 index:  0
position:  3 index:  0

EDIT: Answer That I Found

As many have stated below, useState is asynchronous and will wait for the function to end before updating. The listener is a function that keeps running once it's activated until the next render. Because of this, values only updated when it re-rendered. I decided to switch to useRef as it's synchronous and allowed me to keep the most up-to-date values without re-rendering.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Correct, for a couple of reasons:

  • You create a closure inside addListener capturing the initial value of index, which is set to 0 via useState(0).
  • You supply an empty dependency list for useEffect meaning it will only ever run once when the component mounts

To fix it, you should supply index to the dependency list of useEffect to allow it to re-run thus picking up the newest index.

useEffect(() => { ... }, [index]);

Alternatively, if you find unhooking / rehooking the event handlers is problematic, you can pass a function to setIndex that allows you access to the previous value e.g.

setIndex(oldIndex => {
  let newIndex = oldIndex;
  if (oldIndex != position) {
    // update newIndex 
  }
  return newIndex;
});

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share

2.1m questions

2.1m answers

63 comments

56.6k users

...