1.0.2 • Published 1 year ago

use-effect-without-mount v1.0.2

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
1 year ago

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)
}
  1. Before running the top useEffect mountedRef is false.
  2. Then currentMounted is false and preserves false for the lifetime of the current render.
  3. The top useEffect sets mountedRef to true for the next renders.
  4. Even after recreating useEffect in React 18 the state is preserved and currentMounted equals false
  5. Our bottom useEffect looks at currentMounted, sees false and does not run our effect().
  6. On the next renders, mountedRef is true, so currentMounted is also true.
  7. Our bottom useEffect looks at currentMounted, sees true and run our effect().
  8. For the future of React, return () => {... sets mountedRef to false 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.

My Links