DEV Community

Cleaning up Async Functions in React's useEffect Hook (Unsubscribing)

Elijah Trillionz on November 30, 2021

Functional components in React are most beautiful because of React Hooks. With Hooks, we can change state, perform actions when components are moun...
Collapse
 
joeattardi profile image
Joe Attardi

Using an isMounted approach is somewhat of an antipattern, whenever possible it's better to cancel the request (in the fetch example, you can use AbortController to cancel the request).

Collapse
 
tanth1993 profile image
tanth1993

yeah. I don't like using isMounted, but in React component this is maybe the common way to handle the unmount component

Collapse
 
elijahtrillionz profile image
Elijah Trillionz

Thanks for this. I do know using the mounted approach is probably not the best way, but it's a way around it.
I will try using the AbortController as suggested.
Thanks.

Collapse
 
gnt profile image
gntsketches • Edited

Can you say more about why using the mounted variable is an antipattern? Thanks!

Collapse
 
httpjunkie profile image
Eric Bishard

Like the isMounted solution or not, this article is helpful. It sends the reader down a path of starting to understand the lifecycle (if we can call it that) and gets them thinking about the right way to do things. The comments also suggest some better ideas so all together, I think the reader walks away with more of an understanding. As well, you have done a good job at explaining some intermediate level hooks ideas. Very nice!

Collapse
 
elijahtrillionz profile image
Elijah Trillionz

Thanks you very much.
Really encouraging.

Collapse
 
nokternol profile image
Mark Butterworth • Edited

I should start by saying I like the article and have yet to see anyone come up with an elegant solution to the problem. I agree that the closure variable is an anti-pattern and documented in the react teams blog as one of the most common. I solved this issue using signals.js to create an on-demand pub-sub which could be disconnected on unmount and personally I like the elegance of the execution path.

pseudo-code:
const useAsyncState = <TResult, TArgs>(promise: (...args: TArgs) => Promise<T>, cb: (v: T) => void): (...args: TArgs) => Promise<void> => {
const dispatcher = useMemo(() => new Signal<TResult>(), []);
useEffect(() => {
dispatcher.add(cb);
return () => dispatcher.clear();
}, [dispatcher, cb]);
return (...args: TArgs) => promise(...args).then(dispatcher.emit);
}

usage:
const [state, setState] = useState(undefined);
const loadData = useAsyncState(someAsyncMethod, setState);
useEffect(() => {
loadData();
}, [loadData]);

I wrote the code for an employer so cannot copy and paste it but above is the general idea/approach.
I actually like how the solution turned out and feel that it is clean albeit a little unintuitive as you then execute a Promise<void>

Collapse
 
elijahtrillionz profile image
Elijah Trillionz

Thanks for sharing.

Collapse
 
gnt profile image
gntsketches

Can you link to where the React team documents that the closure variable is an anti-pattern? Thanks!

Collapse
 
nokternol profile image
Mark Butterworth • Edited

reactjs.org/blog/2015/12/16/ismoun...

I should also clarify that I am not a fan of the cancellable promise approach either. IMO, the dispatcher pub/sub link in a hook is the cleanest implementation I've seen to date.

The link above is not actually referring to a closure variable but the general approach of tracking mounted to avoid the condition is the reason ismounted() was cited as being removed and it forming an antipattern.

Collapse
 
yazan_qarabash profile image
Yazan Qarabash

πŸ‘πŸ»πŸ˜

Collapse
 
elijahtrillionz profile image
Elijah Trillionz

Glad you liked it

Collapse
 
rudystake profile image
Info Comment hidden by post author - thread only accessible via permalink
RudyStake • Edited

I am not getting this , can you help me out to figure this one ?
best tantrik in Ranchi

Some comments have been hidden by the post's author - find out more