1.0.2 • Published 3 years ago
use-effect-without-mount v1.0.2
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
useEffectmountedRefisfalse. - Then
currentMountedisfalseand preservesfalsefor the lifetime of the current render. - The top
useEffectsetsmountedReftotruefor the next renders. - Even after recreating
useEffectin React 18 the state is preserved andcurrentMountedequalsfalse - Our bottom
useEffectlooks atcurrentMounted, seesfalseand does not run oureffect(). - On the next renders,
mountedRefistrue, socurrentMountedis alsotrue. - Our bottom
useEffectlooks atcurrentMounted, seestrueand run oureffect(). - For the future of React,
return () => {...setsmountedReftofalsefor 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.