use-stable-reference
TypeScript icon, indicating that this package has built-in type declarations

1.1.2 • Public • Published

use-stable-reference

Simple React hooks to access referentially stable, up-to-date versions of non-primitives.

version bundle size downloads per week license dependencies


Basic usage

import { useStableCallback, useStableValue } from "use-stable-reference";

function Library({ unstableCallback, unstableValue }) {
  const stableCallback = useStableCallback(unstableCallback);
  const getStableValue = useStableValue(unstableValue);

  useEffect(() => {
    if (/* ... */) {
      stableCallback()
      const stableValue = getStableValue()
    }

    // safe to add to dependency arrays!
  }, [stableCallback, getStableValue, /* ... */]);
}

use-stable-reference really shines for library authors or for those writing reusable code. With a library-consumer relationship, the library author can't reasonably expect that the consumer will preemptively wrap any callbacks in a useCallback, or any referentially unstable values in a useMemo. This leaves the author with a few possible choices for how to handle consumer-provided non-primitive arguments:

  1. Leave them out of any dependency arrays, and ignore any eslint React linter warnings/errors
  2. Leave them in the dependency arrays, expecting that the effects / memoizations will run every render
  3. Wrap them in a useStableCallback/useStableValue

With option 3, the returned callback/value-getter are referentially stable, can safely be used in dependency arrays, and are guaranteed to always be up-to-date if the underlying option ever changes! 🎉

API

useStableCallback

useStableCallback accepts one argument, a callback of type: (...args: any[]) => any

useStableCallback returns an up-to-date, referentially stable callback.

useStableValue

useStableValue accepts one argument, a value of type: unknown

useStableValue returns a referentially stable callback that returns an up-to-date copy of the argument.

FAQ

Haven't I seen this before?

A version of this hook has been floating around the React community for a while, often referred to as useEvent or useEffectCallback. This package hopes to distill the best aspects of several different implementations:

Isn't updating a ref in the render method a bad practice?

Updating a ref in the render method is only dangerous when using concurrent features. Consider the following scenario:

  1. A component re-renders, i.e. the render method runs
  2. The ref is updated
  3. The DOM updates are discarded because a second, higher-priority render was triggered
  4. The higher-priority render occurs
  5. Any code which uses the ref value before it's updated in step 6 is using a value from a render that was discarded!
  6. The ref is updated to the intended value

Thankfully, this is rarely something we need to worry about, for a few reasons:

  1. Concurrent mode is opt-in, triggered only when using concurrent features
  2. Concurrent features are only available in React 18+
  3. The React compiler, which will make this library unnecessary, is in beta starting with React 19
  4. The callbacks and values that are passed to useStableCallback and useStableValue may be referentially unstable, but generally have the same behavior from render to render

In other words, for developers using React < 18, there's no issue because concurrent features aren't available; for devs using React > 19, you shouldn't need this package at all because of the React compiler; for those stuck in the middle using React 18, there's a good chance all the ref values will have the same behavior anyway, as long as you pass a callback/value with the same behavior every render.

That leaves just one scenario to consider. For devs using React 18, with concurrent features, with dynamic callbacks/values, consider yourselves warned: your refs may be out-of-sync with your render!

Package Sidebar

Install

npm i use-stable-reference

Weekly Downloads

4

Version

1.1.2

License

MIT

Unpacked Size

10.6 kB

Total Files

7

Last publish

Collaborators

  • elanmed