react-nativereact-navigationuse-state

Why does my state persist on screen change?


I am developing a React Native app and I am currently using a component in 2 different places of my app. I navigate from one place to another using a Drawer Navigator (React Navigation v6). This component updates a state when receiving a response from an API. However, if I switch to the other place where this component gets rendered, the state remains (therefore the visual message from the API appears inside it).

Why does this happen? Shouldn't all states reset on unmount and get the initial value (the one passed to useState()) when the component gets mounted again? This not the behavior I want and from what I know, this was not supposed to happen.

What can I do to make my state not persist on screen change? I have implemented the code below to be executed when a menu screen is selected, but has no effect on this issue:

navigation.dispatch(
        CommonActions.reset({
            index: 0,
            key: null,
            routes: [{ name: targetScreen }]
        })

Solution

  • This behavior in react-native differs from react. This is documented here.

    If you are coming to react-navigation from a web background, you may assume that when user navigates from route A to route B, A will unmount (its componentWillUnmount is called) and A will mount again when user comes back to it. While these React lifecycle methods are still valid and are used in react-navigation, their usage differs from the web.

    Consider a screen A and a screen B. Suppose we navigate from A to B.

    When going back from B to A, componentWillUnmount of B is called, but componentDidMount of A is not because A remained mounted the whole time.

    This is valid for all navigators in react-native-navigation. Thus, if we have a tab bar navigation with 5 tabs, and each tab nests a Stack, then we could navigate on one stack to a certain depth, then switch the tab, and then switch the tab back again, and all screens visited will remain mounted.

    Edit: In response to the comments. The recommended standard pattern to resolve your issue is as follows.

    // some state in my component
    const [state, setState] = useState()
    
    useFocusEffect(
        React.useCallback(() => {
          // set state to initial value
          setState()
        }, [])
    );
    

    This is described here.