Defining a calculated (initialized) constant using React hooks can be performed in two ways that seem functionally equivalent. I don't want to get into the use cases for this, but suffice to say that there are cases where a constant value can be derived from initial props or state that isn't expected to change (think route data, bound dispatch, etc).
First, useState
const [calculatedConstant] = useState(calculateConstantFactory);
Second, useMemo
const calculatedConstant = useMemo(calculateConstantFactory, []);
Both of these seem functionally equivalent, but without reading the source code, I'm not sure which is better in terms of performance or other considerations.
Has anyone done the leg work on this? Which would you use and why?
Also, I know some people will recoil at the assumption that state can be "considered constant". I'm not sure what to tell you there. But even without state, I may want to define a constant within a component that has no state at all, for example, creating a block of JSX that doesn't change.
I could define this outside of the component, but then it's consuming memory, even when the component in question is not instantiated anywhere in the app. To fix this, I would have to create a memoization function and then manually release the internal memoized state. That's an awful lot of hassle for something hooks give us for free.
Edit: Added examples of the approaches talked about in this discussion. https://codesandbox.io/s/cranky-platform-2b15l
useState is simply a state updating function. Const is used here because the change of value is being managed somewhere else by React. You're telling React to manage some value for you by calling useState.
The React useMemo Hook returns a memoized value. Think of memoization as caching a value so that it does not need to be recalculated. The useMemo Hook only runs when one of its dependencies update.
What does useState return? It returns a pair of values: the current state and a function that updates it. This is why we write const [count, setCount] = useState() . This is similar to this.state.count and this.setState in a class, except you get them in a pair.
Here are a couple of cases when you should consider using useMemo: You're noticing a component's render is frustratingly slow, and you're passing a calculation to an unknowable number of children, such as when rendering children using Array. map()
You may rely on useMemo as a performance optimization, not as a semantic guarantee
Meaning, semantically useMemo
is not the correct approach; you are using it for the wrong reason. So even though it works as intended now, you are using it incorrectly and it could lead to unpredictable behavior in the future.
useState
is only the correct choice if you don't want your rendering to be blocked while the value is being computed.
If the value isn't needed in the first render of the component, you could use useRef
and useEffect
together:
const calculatedConstant = useRef(null);
useEffect(() => {
calculatedConstant.current = calculateConstantFactory()
}, [])
// use the value in calcaulatedConstant.current
This is the same as initializing an instance field in componentDidMount
. And it doesn't block your layout / paint while the factory function is being run. Performance-wise, I doubt any benchmark would show a significant difference.
The problem is that after initializing the ref, the component won't update to reflect this value (which is the whole purpose of a ref).
If you absolutely need the value to be used on the component's first render, you can do this:
const calculatedConstant = useRef(null);
if (!calculatedConstant.current) {
calculatedConstant.current = calculateConstantFactory();
}
// use the value in calculatedConstant.current;
This one will block your component from rendering before the value is set up.
If you don't want rendering to be blocked, you need useState
together with useEffect
:
const [calculated, setCalculated] = useState();
useEffect(() => {
setCalculated(calculateConstantFactory())
}, [])
// use the value in calculated
Basically if you need the component to re-render itself: use state. If that's not necessary, use a ref.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With