React-native – useEffect not running when state changes

asyncstoragereact-hooksreact-native

I have a screen (MyPics.js) that shows a collection of images that the user selects from another screen. I get the selected images from asyncstorage.

All works fine if I select images and then reboot the simulator. When I navigate to the MyPics.js screen, I see all of the images stored in AsyncStorage from previous image selections.

  const MyPicsScreen = props => {
    const [picList, setpicList] = useState([]);

    useEffect(() => {
      console.log("I'm in!! picList data is: " +  picList);
      AsyncStorage.getItem("mypics").then((response) => {
      const currentData = JSON.parse(response);
      setpicList(currentData);
      console.log("Current picList data will be: " + currentData);
    });
   }, []);


  return (
<View style={styles.wrapper}>
  <ScrollView>
    <View style={{flexDirection: "column", justifyContent: "center", alignItems: "center"}}>

      {picList ? picList.map((item, index) => {
        return (
            <View key={index} style={styles.viewItem}>
              <Image style={{width: 200, height: 131}} source={Images[PicList[item][3]]} />
            </View>
          );
        }
      ) : <View><Text>You haven't selected any pics yet</Text></View>}

    </View>
  </ScrollView>
</View>
   );
 };

Terminal shows:

  I'm in!! picList data is:  //No data, as expected. Nothing is retrieved yet, then...
  Current picList data: pic001,pic007,pic099 // That shows, correctly, what's stored and screen shows those images 

But if I add images, the new images don't show up unless I reboot the simulator. Plus, no read out in the console, so useEffect is not running to get the data and reset the state.

I'm assuming that going back to the MyPics screen causes a re-render, but I may be wrong.

I made 3 buttons on the MyPics.js screen that manually runs either removeItem or setItem or getItem on asyncstorage which then removes or sets or gets images then updates the picList state.

That works.

When I use the buttons it gets or removes previously set images or sets a new image and the screen re-renders to show the state change.

So, useEffect is not running when I navigate to the MyPics.js screen. It only runs on the first render.

When I add in the picList state as a dependency,

  }, [picList]);

it works but the console is running in an infinite loop.

Is that normal? Should I just ignore the infinitely scrolling console or am I missing a step? Should I be using a focus listener to re-render the page on a re-visit as I only need the new data when I come back to the MyPics screen.

Best Answer

After a lot of pain and wasted time, this works:

 useEffect(() => {
  AsyncStorage.getItem("mypics").then((response) => {
  setpicList(JSON.parse(response);
});
}, []);

const getList = useCallback(() => {
  AsyncStorage.getItem("mypics").then((value) =>{
  setpicList(JSON.parse(value));
});
}, [picList]);

Some very practical examples for functional components can be found in this thread:

React navigation didfocus event listener works differently between class component and functional component

Focus detection is needed when coming back to the screen so that the useEffect function runs again.

Related Topic