3

How to Safely Update React State

 1 year ago
source link: https://blog.bitsrc.io/how-to-safely-update-react-state-daac9e79417
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How to Safely Update React State

Why you can’t perform a React state update on an unmounted component

0*v-H7mul0nMvJLxod

Photo by Markus Winkler on Unsplash

While developing your React application, have you encountered the following errors:

1*RPQk7owVJQVK9zXBTNZLEg.png

As it mentions, you can’t update React’s state in an unmounted component, which has the potential to cause memory leaks. This usually happens in the context of an asynchronous operation or in a subscriber.

For example, when you are waiting to request data and then update the React state, the corresponding component is unloaded. Or when you subscribe to a message source and update the state, it is not cleared in useEffect.

The following is a simple example, please click the button repeatedly to switch the component to show and hide, you will find the error on the console later:

So how do we safely update React state? This can be done with the help of useRef and useEffect:

const mountedRef = useRef(false);

useEffect(() => {
mountedRef.current = true;

return () => {
mountedRef.current = false;
};
}, []);

// if (mountedRef.current) {
// Update your state
// }

useEffect without dependencies can know if the current component is still mounted. Such logic can be reused by different components, so we can write a useMountedState custom hook:

import { useCallback, useEffect, useRef } from 'react';

const useMountedState = () => {
const mountedRef = useRef(false);
const get = useCallback(() => mountedRef.current, []);

useEffect(() => {
mountedRef.current = true;

return () => {
mountedRef.current = false;
};
}, []);

return get;
};

export default useMountedState;

Then in the above example we can use it to solve the BUG:

const isMounted = useMountedState()

useEffect(() => {
sleep(1000).then(() => {
if (isMounted()) {
setText("Hi, SubComponent");
}
});
}, [isMounted]);

In most cases such a useMountedState hook is sufficient, but if we go further, we can write a custom useSafeState hook:

import { useCallback, useEffect, useState, useRef } from 'react';
import type { Dispatch, SetStateAction } from 'react';

function useSafeState<S>(
initialState: S | (() => S)
): [S, Dispatch<SetStateAction<S>>];

function useSafeState<S = undefined>(): [
S | undefined,
Dispatch<SetStateAction<S | undefined>>
];

function useSafeState<S>(initialState?: S | (() => S)) {
const mountedRef = useRef(false);
const [state, setState] = useState(initialState);

useEffect(() => {
mountedRef.current = true;

return () => {
mountedRef.current = false;
};
}, []);

const setCurrentState = useCallback((currentState: S) => {
if (mountedRef.current) {
setState(currentState);
}
}, []);

return [state, setCurrentState] as const;
}

export default useSafeState;

This brings another level of abstraction to our coding. Have fun!

Not a Medium member? Support me here by becoming one.

Build apps with reusable components like Lego

1*mutURvkHDCCgCzhHe-lC5Q.png

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK