jaredLunde/react-hook

useLatest() creates reference to a value from the previous render

Closed this issue · 4 comments

Describe the bug
When I call useLatest, I expect to receive a ref object containing the value I pass to it. The current implementation updates the value with a useEffect callback. This will eventually update the ref, but by then, the code that actually needs the new value to render the component has already run.

To Reproduce

function ComponentWithLatestRef() {
  const value = compute();
  const valueRef = useLatest(value);
  console.log(value === valueRef.current); // `false` when value differs from the last render
  useHook(opts, valueRef); // receives an old value
  
  return <div>...</div>
}

Expected behavior
The ref should be updated inline while the component renders. The existing behavior would be more accurately described by usePreviousAndUpdate(value).

Additional context
This is present in @react-hook/latest 1.0.3

That's how it's supposed to work. What you're doing by reading the current value during the render phase is a side effect.

function ComponentWithLatestRef() {
  const value = compute();
  const valueRef = useLatest(value);
  React.useEffect(() => {
    console.log(value === valueRef.current); 
  }, [value])
  useHook(opts, valueRef); // receives an old value
  
  return <div>...</div>
}

The above will display the correct value. What you're looking for is perhaps the usePrevious() hook.

Lastly, the hook is naming absolutely correct. You're just misunderstanding what it's used for, which is evident if you look at the example in the README. Here's someone else doing the same thing, same name.

Ok, thanks for explaining, I'm not trying to argue with what it's actually intended to do. The linked repo does at least mention that it hooks into the useEffect lifecycle, whereas the wording in the README here only mentions that it updates on each invocation. There's some ambiguity in that, and I don't expect a quick start example to cover all potential use cases.

@jaredLunde Can you explain the reasoning for updating the value in an effect rather than updating it on a render? Like so:

function useLatest(value){
  const ref = React.useRef(value);
  ref.current = value;
  return ref;
}

I've always done it this way and I'm unsure why you would add a lifecycle hook when it doesn't appear to be needed. The only reason I can think is if you wanted to only update the ref if the value changed, but the code has no dependencies so it updates on every render anyway.

You can learn more here:

It ensures the value of current is isolated to the current render. In concurrent mode the same component/hook may render .... concurrently.