'React hooks: how to instantiate instance variable only once

I would like to derive value from my props using react hooks.
I would like to compute this value only once and not on every render.

This is the first solution I came up with but if props changes z is not re-calculated.


function App(props: { x: number; y: number }) {
    
    const zRef = useRef<number | undefined>(undefined);

    if( zRef.current === undefined ){
    
        //Let's assume the computation of x + y is costly, I 
        //want to avoid doing it every render.
        zRef.current = props.x + props.y;
    
    }

    return (<span>{zRef.current}</span>);

}

The second way I found is this one:


function App(props: { x: number; y: number }) {
    
    const zRef = useRef<number | undefined>(undefined);

    useEffect(()=>{

        zRef.current = props.x + props.y;

    },[props.x, props.y]);

    return (<span>{zRef.current}</span>);

}

But the problem is that zRef.current is undefined on the first render.

Any opinion on the matter much appreciated.



Solution 1:[1]

Have you tried with useMemo?

useMemo seems to fit your use case, as it allows you to recalculate a value only when any one of the values specified in the dependency array changes.

Like so:

const z = useMemo(() => {
    return props.x + props.y
}, [props.x, props.y])

Solution 2:[2]

You can use useMemo as descibed in the other answer if you just want to boost performance! If you really rely on only calculating the value once, it sounds as if it would work, but read the docs carefully:

You may rely on useMemo as a performance optimization, not as a semantic guarantee.

In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

So one way to solve your problem is to manually compare if the input has changed and then recalculate the value:

// https://reactjs.org/docs/hooks-faq.html
function usePrevious(value) {
  const ref = useRef();
  useEffect(() => { ref.current = value; });
  return ref.current;
}

function App(props: { x: number; y: number }) {
    const zRef = useRef<number>(0);
    const prevProps = usePrevious(props);
    if (!prevProps || prevProps.x !== props.x || prevProps.y !== props.y){
        zRef.current = props.x + props.y;
    }
    return (<span>{zRef.current}</span>);
}

If you need a cached value like this more often, you could even write your own cache-hook using this trick.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Joseph Garrone
Solution 2 Clemens Frahnow