Search code examples
javascriptreactjsreact-hooksmemoization

useState, UseRef, or useMemo, what should I prefer


I have a scenario where I need to store the output of a function throughout the component life cycle(this value should never change).

consider the example below

const UniqueIdView = () => {
    const [uniqueIdState1] = useState(() => uniqueId('prefix_'))
    const [uniqueIdState2] = useState(uniqueId('prefix_'))
    const uniqueIdRef = useRef(uniqueId('prefix_'))
    const uniqueIdMemo = useMemo(() => uniqueId('prefix_'), [])
    return (
        <div>
            {uniqueIdState1}
            {uniqueIdState2}
            {uniqueIdRef.current}
            {uniqueIdMemo}
       </div>
    )
}

which out of the 4 approaches mentioned above is ideal?

My understanding:

useState should be used to store values where the change in value should trigger re-render.

useMemo should be used in case I want to memoize the calculation, and memoization always has an associated cost.

So, useRef in my opinion is suitable.

But, I then have one confusion:

useRef will trigger my function again and again on every re-render while using the callback approach with useState will trigger my function only once.

But again, If I have to think of the cost of calling the function again and again, should I use useMemo (but in this case, the function is not complex, should we add the memorization overhead)?

Update

What do I want to achieve?

I want to create a custom hook that should return uniqueId which should not change on re-render

const UniqueId = () {
    const uniqueId = useStableUniqueId('prefix__')
    return <div>{uniqueId}<div>
}

so no matter how many time UniqueId re-renders the value should not change.


Solution

  • The value passed to useRef is only the initial value, but if it's a function invokation it will actually be called each render. Not so sure about the rest of your question. Each hook exists for a specific purpose. Pick the one that serves your needs.

    I have a scenario where I need to store the output of a function throughout the component life cycle.

    To me the clear choice is the useMemo hook to memoize the result value of a possibly expensive function call.

    It's not regularly updated so useState doesn't fit. If you decided to store it in state and ever needed to updated it, you would need an useEffect hook with dependency and recompute a new value and call the state updater function. This is essentially the useMemo hook.

    If you decided to store it in a React ref then you'd again need to pair that with a useEffect with a dependency to update the ref.current value to keep it updated, and this, again, essentially gets you the useMemo hook.

    Update

    Since you are really looking to optimize a custom hook that provides a static unique id for the life of the component:

    1. Using useMemo

      const useUniqueId = (prefix = 'prefix_') => {
        return useMemo(() => uniqueId(prefix), []);
      };
      
    2. Using useState

      const useUniqueId = (prefix = 'prefix_') => {
        const [uniqueId] = useState(() => uniqueId(prefix));
        return uniqueId;
      };