HartleySan
HartleySan

Reputation: 7810

Why am I getting the following React Native warning: Can't perform a React state update on an unmounted component

I'm trying to build a React Native app, but am still kind of new to the React/RN ecosystem, so I may just be misunderstanding something obvious with the problem I'm having.

I have an app where a lot of the pages are structured as follows:

<View>
    <NavComponent />
    <View>
        {/* Page component structure/logic here */}
    </View>
</View>

NavComponent loads a toggleable nav menu with TouchableOpacity elements like the following:

Go to Screen #1
Go to Screen #2
Go to Screen #3

The problem I'm having (and maybe this isn't a problem so much as just how React/RN works) is that if I start on screen #1, open the nav, go to screen #2, open the nav again, and then go back to screen #1, even though screen #1 shows up again, the actual rendering function for screen #1 doesn't seem to be called again, and since NavComponent is part of the rendering of each screen, when I try to open the nav from screen #1 again, I get the following warning:

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, a useEffect cleanup function ...

Again, maybe my approach with the app is flawed to begin with, but essentially, when I go from one screen to another from the nav, I always want the new screen to re-render from scratch (including the initial Ajax call for data).

Here's a more fleshed-out example of the render function for a screen (they all follow this same basic pattern):

const screen1 = ({ navigation }) => {
    const [serverData, setServerData] = useState(null);

    useEffect(() => {
        // getPageData is a custom method I added to axios.
        axios.getPageData('/api/url/here', (data) => {
            setServerData(data);
        });
    }, []);

    if (serverData) {
        const { meta, user, data } = serverData;

        return (
            <View>
                <NavComponent />
                <View style={styles.container}>
                    {/* Page component structure/logic here */}
                </View>
            </View>
        );
    }

    return null;
};

If, for example, I added a console.log to the beginning of the render function above, it's called the first time the screen is loaded, but if I go to screen #2 and then come back to screen #1 via the nav component, the console.log isn't output again. Why?

And for what it's worth, I'm using the standard navigation.navigate('ScreenName') in NavComponent to go from screen to screen.

Any advice on how to fix the warning (and/or just better design the app) so that I can have that nav on every page would be greatly appreciated. Thank you.

Upvotes: 0

Views: 120

Answers (1)

Ahmed Khattab
Ahmed Khattab

Reputation: 2799

your api call is resulting in the warning, in the react/native ecosystem, when a component is removed from the tree, the developer needs to cancel all subscriptions (listeners to events) and async tasks(fetching data from the web), those function need to be canceld by the developer as react/native cant do that for you.

to handle that in a class based component, you need to impelment componentWillUnmount and remove the subscriptions there

class MyClass extends Component {
   componentWillUnmount() {
     // remove listeners and cancel requests
    }

but in a modern hook component , you need to return a cleanup function, a function to return in useEffect that will be called by react to cancel any subscriptions you have made, in your case, just return a cleanup function should remove that warning for you


  const [mounted, setIsMounted] useState(false)
 useEffect(() => {
        // getPageData is a custom method I added to axios.
        setIsMounted(true)
        axios.getPageData('/api/url/here', (data) => {
            if(isMounted)
             setServerData(data);
        });

        return () => {
             setIsMounted(false)
         }
    }, []);

Upvotes: 1

Related Questions