2021-08-28

New React hook for callbacks

I've been using React for a couple years, with functional components and hooks. The most frustrating thing I've found has to do with passing callbacks. I have two concerns:

  1. If an element receives a callback from a parent it can't really use that function in a event listener or onEvent field or in a setTimeout/setInterval, because the passed function may be redefined any time the parent rerenders.

  2. A function that's generated at each render (like an arrow function) and passed to a child element breaks React.memo().

I've seen some limited solutions to this, such as useEventListener() but not one that addresses all the situations that concern me.

So I've written my own hook that I've called useHandler. Unlike useCallback, which generates a new function whenever the inputs change, useHandler always returns the same function. This way the function case be passed to an element using React.memo() or to a setInterval or whatever.

It call be used by the parent element (needed for the React.memo case) or by the child element (if you're worried that a passed-in function might change). Here's an example of it used in the parent:

const onClick = useHandler(event => {
  //Do whatever you want.  Reference useState variables or other values that might change,
})

<MyElement onClick={onClick} />

MyElement now always gets the same function, but calling that function will execute the most recent code in the parent element.

Here's an example in the child element:

const reportBack = useHandler(props.reportBack)

useEffect(() => {
  setInterval(reportBack, 1000)
}, [reportBack])

The interval callback will always call the current reportBack passed from the parent.

The only thing missing for me is that I haven't modified the React eslint config to recognize that that results of useHandler cannot change and thus shouldn't generate a warning if omitted from the dependency list in useEffect or similar.

And finally, here's the source for my onHandler hook:

export function useHandler(callback) {
  const callbackRef = useRef()
  callbackRef.current = callback
  return useRef((...args) => callbackRef.current(...args)).current
}

My questions are: Are there any flaws in this solution? Is it a good way to address my issues with React callbacks? I haven't found anything more elegant but I'm plenty willing to believe that I've missed something.

Thanks.



from Recent Questions - Stack Overflow https://ift.tt/3sQOfz7
https://ift.tt/eA8V8J

No comments:

Post a Comment