landvibe
landvibe

Reputation: 181

how to use useRef to reference latest value

People use useRef to save latest value like this code

function MyComponent({ value }) {
  const valueRef = useRef();
  useEffect(() => {
    valueRef.current = value;
  });
}

I know useEffect needs for concurrent mode.

and useEffect is executed in the order of the definition

function MyComponent({ value }) {
  useEffect(() => {
    console.log('log1');
  });
  useEffect(() => {
    console.log('log2');
  });
}
// result is
// log1
// log2

so in the below code, log1 is printed with old value and log2 is printed with new value

function MyComponent({ value }) {
  const valueRef = useRef();
  useEffect(() => {
    console.log('log1', valueRef.current);
  });
  useEffect(() => {
    valueRef.current = value;
  });
  useEffect(() => {
    console.log('log2', valueRef.current);
  });
}

I think it's weird because the value is different according to the position.

What is the right way to use useRef to reference latest value? (FYI I know it's better to use deps than to use useRef)

Upvotes: 18

Views: 107840

Answers (4)

Hossein Mohammadi
Hossein Mohammadi

Reputation: 1473

Another approach is change ref value after if condition.

  const valueRef = useRef();
  useEffect(() => {
    console.log('log1', valueRef.current);
  });
  if(valueRef.current !== value) {
    valueRef.current = value;
  };
  useEffect(() => {
    console.log('log2', valueRef.current);
  });

Upvotes: 4

Sang
Sang

Reputation: 4446

To get the latest value from all code positions (render, useEffect body, disposal function body), you have to use at least two useRefs.

Here is the code

export const usePrevRef = value => {
    const currentRef = useRef()
    const prevPref = useRef()
    prevPref.current = currentRef.current
    currentRef.current = value
    return prevPref
}

enter image description here

If you use one useRef, and place the ref.current = value update in ③(or⑤), then the value won't be correct if called from ④and⑥(or⑥).

I wrote a blog on this. Please feel free to check.

Upvotes: 1

windmaomao
windmaomao

Reputation: 7671

It really depends on what you mean by latest value, your code does store latest value to valueRef.current.

  const valueRef = useRef();
  useEffect(() => {
    console.log('log1', valueRef.current);
  });
  useEffect(() => {
    valueRef.current = value;
  });
  useEffect(() => {
    console.log('log2', valueRef.current);
  });

If you do want to have a more "stable" value, then you should use setState.

  const valueRef = useRef();
  const [value, setValue] = useState(null);

  useEffect(() => {
    setValue(1)
    console.log('log1', valueRef.current);
  });
  useEffect(() => {
    // value is still 1
    valueRef.current = value;
  });
  useEffect(() => {
    // value is still 1
    console.log('log2', valueRef.current);
  });

IMHO, useRef is a very difficult topic, it was designed for Dom ref initially. But then people find it useful when it comes to decouple the variable from the rendering state/prop. You can't actually say useRef always store the latest value, it actually still store the regular value, except it won't trigger re-render, since the reference to this ref is fixed after initialization (only ref.current is changing).

Upvotes: 17

Drew Reese
Drew Reese

Reputation: 202706

The idiomatic way is to store some "current" value for the next render and retrieve the current ref value (the previous value). Hooks FAQ: How to get the previous props or state

const prevValueRef = useRef();
useEffect(() => {
  prevValueRef.current = value; // cache current value for next render
});
const prevValue = prevValueRef.current; // get previous value from last render

Running sandbox demo

Edit useRef-previous-value

Upvotes: 1

Related Questions