Reputation: 165
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.
Upvotes: 4
Views: 11744
Reputation: 1395
Thank you @Kelvin Aitken and @Danny Buonocore for support
I wondered why useEffect
hooks only work during initial loading and failed whenever I tried to paginate more items.
I later found that I should pass any dependency/state in []
to tell hooks to render anytime state changes.
const [fetchItemInput, SetFetchItemInput] = useState(input);
const fetchMoreItemBtn = (nextCursor) => {
const newInput = { ...fetchItemInput };
newInput.after = nextCursor;
return SetFetchItemInput(newInput);
};
useEffect(() => {
handleFetchItem(fetchItemInput)
.then((res) => {
...
})
.catch((error) => {
...
});
return () => {};
}, [fetchItemInput]); // Any changes run the effect
Thank you again for your support.
Upvotes: 1
Reputation: 443
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:
Focus detection is needed when coming back to the screen so that the useEffect function runs again.
Upvotes: 2
Reputation: 3777
From the docs
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.
So passing an empty array to your useEffect
is roughly equivalent to (except useEffect
fires after the initial paint) the componentDidMount
and componentWillUnmount
functions of class components.
Upvotes: 2