How to memoize custom React hook
Asked Answered
E

2

24
const useSomeHook = ({number}) => {
  const [newNumber, setNewNumber] = useState(0)

  useEffect(() => {
    setNewNumber(number + 1)
  }, [number])
}

const SomeComponent = ({number, value, ...restProps}) => {

  useSomeHook({number})


  return <div>{number}</div>
}

Let's imagine I have this case. Each time when in SomeComponent come some new prop, it will call my useSomeHook hook, but I want to prevent it. I want to call it only when the number is changed (memoize it). In other cases, don't touch it. But I haven't found any solving with this case. Could you help me solve this issue?

Earn answered 26/4, 2020 at 9:28 Comment(5)
The useEffect hook within useSomeHook will only be invoked when the dependency number changes.Recording
So I don't see what memoization gives you in this case.Recording
In this case it will be ok, but imagine that I have some hard logic in this hook, and it invoked each time for example I type something with redux form , and it works horribleEarn
What you pass in the second argument for useEffect will trigger subsequent invocation of the callback you pass in as the first argument. To address your ask, you can do two things: 1. Add in-depth equality check as the second param (this requires some work), and/or 2. Add a condition in the callback; ex: if (obj.a !== obj.b) callback()Waterscape
use useRef instead of useStateKatlin
A
12

You can not prevent calling hook, this will lead to invariant violation error. Every hook in component should be executed on each render. You should rely on useEffect dependencies argument to run conditional code only if value changes.

Adminicle answered 26/4, 2020 at 9:42 Comment(0)
K
3

use useRef instead of useState to memoize/cache the returned value so that its not recomputed when its used across components, or when there are multiple instances of that component

const useSomeHook = ({number}) => {
  const numberRef = useRef(0)

  useEffect(() => {
    numberRef.current = numberRef.current + 1
  }, [number])
}

const SomeComponent = ({number, value, ...restProps}) => {

  useSomeHook({number})


  return <div>{number}</div>
}

it also sounds like you want to use useMemo instead of useEffect

const useSomeHook = ({number}) => {
  const numberRef = useRef(0)

  cachedValue = useMemo(() => {
    return numberRef.current + 1
  }, [number])
   return cachedValue
}

const SomeComponent = ({number, value, ...restProps}) => {

  const cachedValue = useSomeHook({number})


  return <div>{number}</div>
}
Katlin answered 28/4, 2023 at 10:10 Comment(2)
what if our useSomeHook is making an api call, it that case useRef might not be a good thing to use as well, thoughts ?Abampere
@Abampere Why not? I would argue that using useRef especially when making api calls is even better because you can use it to cache the api call response, otherwise if you use the same hook across multiple components, you would be making multiple api callsKatlin

© 2022 - 2024 — McMap. All rights reserved.