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.
Related
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.
Trying to understand, if simple react component without any props, that has been created as
const x = <div />
will be rendered in a more efficient way during tree comparison than memorized component?
const y = React.memo(<div />)
will be rendered in a more efficient way during tree comparison than memorized component?
There's no one good answer to that, because it depends on the case. If a component does not include any heavy logic, it's pointless to wrap it with memo because wrapping with memo uses memory and slows down your performance a bit.
In your particular example that you have shown in codesandbox, there's clearly no point to wrap your component with memo since there is not any complex logic inside.
And once again - in your particular case memo is pointless and will run slower than the component without memo (because the built-in shallow comparison of props has to take place). However, the differences in performance will be practically unnoticeable.
This question already has answers here:
Is it a good idea to memoize all of my react components?
(2 answers)
Closed 2 years ago.
I know that using React.memo() to memoize components / parts of components is generally a good practice, but I find myself memoizing every single field on forms implemented using React Hook Form. Since RHF uses uncontrolled components, it does not seem to be a problem: the components seem to update just fine, and I do see a performance improvement.
Still, my instinct tells me that memoizing almost everything is a bad practice. Is it a bad practice? Is there some other way that I should economize on renders?
Your instinct is correct. If React.memo() everything makes React more efficient, it should be a default behavior, not only an optional (like pure function).
For shorter explanation:
React.memo() prevents some redundant rerender by comparing the component's previous and current props.
<Parent>
<Child />
</Parent>
Normally, when the parent rerenders, the child rerenders as well by default.
If the props passed into Child are not changed when Parent rerender, Child does not rerender if you use React.memo() on Child
The question here is: why React knows props on Child was not changed in the current rendering phase?
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 rerendering time.
You only need to use React.memo() for some huge things (Table with a lot of rows and for some reasons it does not have pagination).
I would say YES it is pretty redundant to memoize everything. I would recommend using react profiler, to know which components need memoization.
I would refer to this answer as well.
https://stackoverflow.com/a/54024157/7174241
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.
the documentation says
All React components must act like pure functions with respect to their props.
https://facebook.github.io/react/docs/components-and-props.html, but does not explain the real reason behind it, why is that?
A React component should be pure, this means the result of its render method should depend solely on the props and the state, and for the same properties and state render should give the same result.
If render is not pure, it means it can return different results for the same input, so React cannot tell which parts of the DOM need to be updated based on the changes to the component. This is critical as the performance of React depends of this. Why? That's a bit complex.
It is amazing to define the UI based on a state, and have the UI re-render itselfs every time any part of the state changes. But you can imagine doing a complete re-render of the entire DOM every time you change something would be painfully slow.
React solves this by checking the minimum ammount of changes needed o the DOM to reflect the new state. It knows what those changes are based on what properties and state each component receives, and can tell if it needs to update a component if any of its properties or state changed.
Take this tree as an example of a component hierarchy
Here we changed h to 8, so we also changed f because h is a child of f, and we also changed c because f is child of c and etcetera.
The key here is to think how React checks the component tree. It ill start at the root and see it changed. Then it'll check all the children and realize only c changed, so there is no need to check all the a and b branches. Then it'll check the c branch and realize only f changed so there is no need to check e and g. That operation is done on every component to calculate the minimum ammount of changes and also what needs to be updated.
If at any point you could mutate how a component is rendered it means React will need to check all of the branches and all of its children to know what changed because it can't rely on the state and the props to know when a branch changed and how. This would be painfully slow and make the whole React framework inviable.
I would say because of tracking the component state changes. If it isn't pure, it would cause side-effects every time it is executed. That way, would be very hard to know what has changed and furthermore how to react to these changes.
Pure functions, in other way, have the same output with the same input. Making it a lot easier to manage properties and track when something has changed, resulting a easier and predictable way to react to the change.
If they weren't pure functions in relation to their props then it would be violating the entire heirarchy/delegation structure that react provides and relies on.
Lets say you have two components, component A and Component B, and Component A is the parent to Component B. Component A has its own state based on some sort of data. When you pass a part of its state down as a prop to component B, you are establishing a contract between the two components that component B will delegate to component A to get the value of said prop.
This is in a sense a contract between the two components and the only way the contract isn't violated is that component B doesn't directly alter or change the passed down prop. That is what being a pure function means, that it doesn't mutate the prop directly. Of course you can clone the prop and then change it however you want that isn't a breaking of contract since at that point they aren't referencing the same values. But if you do mutate props directly you will also be mutating the parent component value. This can cause unintended side effects as well as cause issues with the react shadow dom differencing algorithm.
Here is that explained from the official react docs
https://facebook.github.io/react/blog/2015/02/24/streamlining-react-elements.html#problem-mutating-props-you-dont-own
You will discoverer "Why" understanding Reconciliation algorithm React uses for rendering.
Here you have all the information needed to understand what you want.
Part of that is well explained in Marco Scabbiolo's answer, but If you want to understand the way React works I strongly recommend you to read the post I've suggested.
Posting the answer here would be too much for a post and unnecesary because It has already explained by React Team. That's why I prefer giving you the source directly.