use-effect-without-mount
v1.0.2
Published
React Hook useEffect without running on mount. Compatible with React 18
Downloads
56
Readme
React Hook useEffect without running on mount.
:boom: Compatible with React 18. And below and adove.
Usage
Just use like default useEffect
.
import { useEffectWithoutMount } from 'use-effect-without-mount'
useEffectWithoutMount(() => {
// your code
}, [yourDeps])
Even when React starts preserving state after a remount, useEffectWithoutMount
will not run on every mount.
There is also useLayoutEffectWithoutMount
:
import { useLayoutEffectWithoutMount } from 'use-effect-without-mount'
How it works?
React 18 does not remount and mount your components in development mode.
React 18 only destroys your useEffect and useLayoutEffect and creates them again. Nothing more.
And since all the data in one render is immutable, we can look at it to check if there was another render and that out render is not mounting render.
import { useEffect, EffectCallback, DependencyList, useRef } from 'react'
export function useEffectWithoutMount(effect: EffectCallback, deps?: DependencyList) {
const mountedRef = useRef(false)
// It's the key to solve our problem
const currentMounted = mountedRef.current
useEffect(() => {
mountedRef.current = true
return () => {
// Today we don't need it, but in the future of React — we will need.
mountedRef.current = false
}
}, [])
useEffect(() => {
if (currentMounted) {
return effect()
}
}, deps)
}
- Before running the top
useEffect
mountedRef
isfalse
. - Then
currentMounted
isfalse
and preservesfalse
for the lifetime of the current render. - The top
useEffect
setsmountedRef
totrue
for the next renders. - Even after recreating
useEffect
in React 18 the state is preserved andcurrentMounted
equalsfalse
- Our bottom
useEffect
looks atcurrentMounted
, seesfalse
and does not run oureffect()
. - On the next renders,
mountedRef
istrue
, socurrentMounted
is alsotrue
. - Our bottom
useEffect
looks atcurrentMounted
, seestrue
and run oureffect()
. - For the future of React,
return () => {...
setsmountedRef
tofalse
for the next mount.
So our bottom useEffect
will only work on the next renders (not the first "mounting render") and even in the future of React.