I’ve read the documentation about memoization techniques in React and have a feeling that I understand them correctly.
However, I see projects where useMemo and useCallback are applied in different ways.
I would like to confirm that I’m not missing anything, so I will describe my understanding below. Please, tell me if this makes sense or correct me if I’m wrong :)
useMemo
This hook is used to cache the result of some calculation between re-renders.
const cachedValue = useMemo(() => calculateValue(), dependencies)
It means that after the first run, the cachedValue is saved in memory and returned on subsequent renders without any calculations if dependencies remain the same. The cachedValue object on the will be the same (same reference) and would return true on Object.is().
Memoizing results of expensive computations improves performance because they are not calculated even if the component is re-rendered, but at the cost of running the hook itself. (Not sure about that)
Additionally, it is used to prevent re-renders when passing these values as props to a component memoized with React.memo(Component) or as dependencies to useEffect hook.
useCallback
This hook caches the function definition (not the result of its work) between re-renders.
const cachedHandler = useCallback(() => { doSomething(); }, dependencies);
The function is defined and saved (but not called) in the variable on the first run, and the same definition is returned on subsequent re-renders if dependencies remain the same.
Since the function is not called when this hook is used and no results are cached, there are no immediate performance gains. (Not sure about that)
The benefit is only when this cached function is passed as a prop to a component memoized with React.memo(Component) or as a dependency to useEffect.
When a memoized function is passed as a prop inside an inline function (<Component onSomething={() => cachedHandler()} /> there are no performance gains since a new anonymous function is created and passed every time. (Saw this usage a few times).
React.memo
A component wrapped in React.memo is cached and skips re-renders as long as its props remain the same. There is no need to memoize primitive values (strings, for example), but for objects and functions, you need to apply either useMemo or useCallback hooks.
General notes
A lot of projects and developers wrap values and functions in these hooks prematurely either by convention or as a precaution ("if we memoize everything, there is no need to think about it in the future and the cost is not so high").
The documentation and React developers advise against using these hooks until it is necessary (gives measurable performance gains) as using them is not free and adds cognitive complexity (harder to read and follow the code).
This description does not cover all the cases, but can be applied to most simple cases.
Related
In my react js application i have a reduce function that is responsible to loop trough an array of 4 strings and to return a string with all these items. Is there a reason to wrap the function with useMemo hook or it is redundant in this situation?
useMemo caches the result of the given function so It's not recalculated on every re-render.
Whether to memoize depends on how often the function's dependencies change. Still it's unlikely that you need to recalculate the value on every render, so generally its a good idea to wrap it with useMemo
I am recently refactoring a web app using React Hooks. I encounter a problem regarding useCallback. Based on description of Kent: https://kentcdodds.com/blog/usememo-and-usecallback, useCallback is to pass in identical function reference to sub-components, to avoid re-render of sub-components, so that the performance is better. However, it's used together with React.memo. And as Kent said:
MOST OF THE TIME YOU SHOULD NOT BOTHER OPTIMIZING UNNECESSARY RERENDERS. React is VERY fast and there are so many things I can think of for you to do with your time that would be better than optimizing things like this. In fact, the need to optimize stuff with what I'm about to show you is so rare that I've literally never needed to do it ...
So, my question is: am I right to claim that we do not need to use useCallback usually? except when the callback is expensive to create, using useCallback avoids re-creating the callback for every render.
Say, for a onClick or onChange event handler, 2 lines or less, shall we just not use useCallback to wrap it?
I find the useCallback() is necessary when I don't want the function reference to change. For example, when I'm using React.memo on some child component that should not be re-rendered as a result of a reference change in one of its methods that comes through props.
Example:
In the example below Child1 will always re-render if Parent re-renders, because parentMethod1 will get a new reference on every render. And Child2 will not re-render, because the parentMethod2 will preserve its reference across renders (you can pass a dependency array to make it change and be re-created when new input values come).
Note: Assuming the Child components are being memoized with React.memo()
function Parent() {
const parentMethod1 = () => DO SOMETHING;
const parentMethod2 = useCallback(() => DO SOMETHING,[]);
return(
<React.Fragment>
<Child1
propA=parentMethod1
/>
<Child2
propA=parentMethod2
/>
</React.Fragment>
);
}
On the other hand, if the function is expensive to run, you can memoize its results using the useMemo hook. Then you will only run it when new values come, otherwise it will give you a memoized result from previous calculations using those same values.
https://reactjs.org/docs/hooks-reference.html#usecallback
useCallback
Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).
useMemo
Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
I think you are right. From how it's designed, useCallback should be almost useless in React. It can not be used directly to prevent a child render.
What can save a child render is to wrap entire render using a useMemo.
const Title = () => {
...
const child = useMemo(() => {
return <Child a={"Hello World"} />
}, [])
return (
<>
{child}
<div onClick={onClick}>{count}</div>
</>
)
}
The above approach is a bit different than React.memo because it acts directly on the parent Title, instead of Child. But it's more or less answer your question why it's useless, except when you use it as the shortcut for useMemo.
Article explaining this, https://javascript.plainenglish.io/can-usememo-skip-a-child-render-94e61f5ad981
back to useCallback
Now let's go back to see if a callback wrapped with or without useCallback is useful.
<div onClick={onClick}>kk</div>
The only thing it might save is that when it's under reconciliation, onClick (with useCallback) points to the same function instance.
However I don't know if React actually does any optimization at that step. Because assigning a different callback to the attribute might take additional memory and time. But adding a new variable in general takes additional memory as well.
So this type of optimization is more like a coding optimization, more or less subjective. Not objective enough to be applied in a solid case.
Of course, if you want to fix a function instance for any third party function, ex. debounce. That might be a good use, but still smell fishy, because useMemo seems much more versatile to cover this case as well.
All in all, I'm only pointing out, useCallback isn't doing what's the public believe it can do, such as to bailout child component.
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
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.
I've recently decided to refresh on react's documentation and came across https://reactjs.org/docs/react-api.html#reactmemo (React.memo).
At first glance it looked like a good candidate for pretty much all of my function components. So I did just that and applied memo on all of them.
I'm not sure if I am able to notice dramatic performance boost, but it for sure didn't make anything slower.
Hence the question: should I apply memo to all my fucntion components by default, just in case so I can catch edge cases of re-rendering a lot of elements? Are there any drawbacks to this / something else i need to be aware of?
No, you shouldn't memoize all your function components by default. You should decide when to do this on a case-by-case basis.
1. It's only for "pure" function components (as in, without side effects)
From the documentation:
If your function component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result.
Imagine that you add some side effect to your function component, for example, reading the current time from Date.now(). The component will work as expected the first time it's called with certain props, but the next time it's called with those same props, the old time will be used. This can lead to confusing bugs.
2. Not all pure function components will benefit from memoization
For example, if it's unlikely a component will ever be called with the same props, then memoizing the result will likely lead to worse performance. After all, memoization is just storing the result of a function call to an object, where the key is derived from the input to the function and the value is the result of the function call. Why store this if you'll never use it?
3. Memoization is more useful for functions that are computation intensive
If your function component doesn't take many CPU cycles to render, then any performance optimization is likely going to be negligible. But it's now possible to later add side effects to that component, forget that it's being memoized, and waste time fixing the resulting bugs.
Again, from the docs:
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
For shorter explanation:
If React.memo() everything makes React more efficient and optimized, it should be considered as a default behavior, not only an option (like pure function).
Basically, React.memo() prevents redundant rerenders by comparing the component's previous and current props.
<Parent>
<Child />
</Parent>
Normally, when the Parent rerenders, the Child rerender as well by default.
With React.memo(), if the props passed into Child are not changed when Parent rerender, Child does not rerender.
The question here is: why does React know props passed to Child was not changed in the current rerender?
The answer is: React itself has to do a comparison, and it is the cost of React.memo()
If the time to comparison > the time to just rerender the whole Child component, it even slows down your app.
Is putting functions in render like this a bad practice?
Are these functions created every render, leading to a performance hit? How big of a performance hit? Is there a link somewhere that measures this or a reliable source saying that this is bad?
Does this cause unnecessary renders in any way? I hear this is especially not a good idea for PureComponent.
Having trouble finding clarification whether this is okay or not.
class MyComponent extends Component {
...
render() {
const myFunc1 = () => {};
const myFunc2 = () => {};
const myFunc3 = () => {};
const myFunc4 = doSomething(() => {});
return (
<div>hello world</div>
);
}
}
Is putting functions in render like this a bad practice?
Yep
Are these functions created every render, leading to a performance hit?
Yes
How big of a performance hit?
It can vary from totally negligible (most of simple components) to absolutely critical (if you constantly re-render your whole tree on a complex app).
Is there a link somewhere that measures this or a reliable source saying that this is bad?
See Chase DeAnda's answer.
Does this cause unnecessary renders in any way? I hear this is especially not a good idea for PureComponent.
Indeed, since the function will be created at every render, if you pass them to a PureComponent, it will think the function changed and will render again.
Having trouble finding clarification whether this is okay or not.
I hope this helps.
More like a bad practice. It won't break your code, but can cause some serious performance issues. From the React Docs
The problem with this syntax is that a different callback is created
each time the component renders. In most cases, this is fine. However,
if this callback is passed as a prop to lower components, those
components might do an extra re-rendering. We generally recommend
binding in the constructor or using the property initializer syntax,
to avoid this sort of performance problem.
So what you have in the example is fine because you aren't passing it down as a prop. If you passed myFunc4 to the div as a prop, then you would see some performance issues.
Is putting functions in render like this a bad practice?
Well it certainly isn't good practice, but from a performance perspective it's entirely project-dependent.
Are these functions created every render, leading to a performance hit? How big of a performance hit?
Yes, the functions are created on every render call, and if you are doing anything slightly more complex than this year's 600th job-search toy and you are calling your render repeatedly and rapidly (e.g. in a 60 FPS render cycle), the newly created function objects will have a minor impact on performance as the garbage collector will have to run more frequently.
This is again project-dependent and opinion based, but we have a rule to prefer creating non-primitive objects outside any render calls (or any lifecycle methods that are directly associated with render, such as componentDidUpdate).
Does this cause unnecessary renders in any way?
Simply creating function objects inside render won't cause additional unnecessary renders by itself, but as I mentioned, the function objects are created with every render call, which can be more frequent than you might've expected at first.
If you are also passing down such a function object as a callback or handler to a child component, remember that shouldComponentUpdate implementations which do a shallow reference check on props will trigger, because with each render you are passing down a different function object reference.