Is this an accurate polyfill of react's useEffectEvent?
Asked Answered
C

1

13

Is this an accurate polyfill of react's upcoming useEffectEvent hook? Does it present any potential issues?

One part I was not sure about was whether the ref is guaranteed to have been updated prior to the returned function being used in any other effects. I think I'm okay on this front so long as I never use the rather esoteric useInsertionEffect anywhere else in my code, but I would like to confirm that.

function useEffectEvent(callback) {
  const fnRef = useRef(null)
  useInsertionEffect(() => {
    fnRef.current = callback
  }) 
  return (...args) => {
    return fnRef.current.apply(null, args)
  }
}
Collaborate answered 25/5, 2023 at 18:17 Comment(0)
C
13

I don't think the React team has proposed an official polyfill for useEffectEvent (at least I haven't seen it).

Having said that, you can see a polyfill inside the RFC for the now defunct useEvent which initially used useLayoutEffect. And in an early version of Separating Events from Effects, Dan Abramov showed an updated version that looked like this (no longer visible in the docs):

import { useRef, useInsertionEffect, useCallback } from 'react';

// The useEvent API has not yet been added to React,
// so this is a temporary shim to make this sandbox work.
// You're not expected to write code like this yourself.

export function useEvent(fn) {
  const ref = useRef(null);
  useInsertionEffect(() => {
    ref.current = fn;
  }, [fn]);
  return useCallback((...args) => {
    const f = ref.current;
    return f(...args);
  }, []);
}

This polyfill uses useInsertionEffect. I believe that the reason for this choice is that insertion effects are the first to run (compared with useLayoutEffect and useEffect). So it is pretty safe to assume that the ref is updated before any of your other effects run.

Later on, the RFC for useEvent was shelved and useEffectEvent started to show in the docs. Nonetheless, the same polyfill can be reused to implement useEffectEvent since the exposed behaviour is the same.

Please note that with this polyfill, the hook returns a stable function (thanks to useCallback(..., [])), which might not be necessary but is more fool proof (in case the consumer adds the returned function to the dependencies of the effect by mistake).

If you want to understand more about the polyfill, I've blogged about it in: A look inside the useEvent polyfill from the new React docs.

Cutin answered 20/6, 2023 at 12:57 Comment(2)
Fantastic. Do you see any pitfalls using this polyfill in production code today? The React team has been heading this direction for about 2 years, so it seems very likely this is the solution React will land on.Kinfolk
I don't see many pitfalls. The actual hook might run at a slightly different time compared with the polyfill, but unless you use other Insertion Effects which depend on the result of the code inside useEvent, you're unlikely to see a negative effect.Cutin

© 2022 - 2024 — McMap. All rights reserved.