Optimize the performance of the app is a very extensive subject. The question’s title relates to optimizing rendering, and this can be taken to two interpretations: avoid unnecessary rendering or make rendering computationally less costly.
The body of the question demonstrates a concern with declaring functions or constants in the functional component, which are re-declared in every rendering. That will be the focus of the answer written here.
When re-rendering occurs?
It is important to know when a re-rendering occurs before trying to optimize it. The author of the question already seems to know about, but follows a brief summary. Re-rendering occurs when:
The root node of the tree is of different type (e. g. <div> <a>Oi</a> </div>
and <button> <a>Oi</a> </button>
);
Some attribute has been modified (e. g. <div className="a" />
and <div className="b" />
);
Some component property has been modified (e. g. <Timer time={10} />
and <Timer time={15} />
);
Some state of the component has been modified;
The comparison of child nodes finds differences. This comparison is a recursion on the two trees at the same time, comparing node to node.
Source of the quote: What makes my component render again in React?
How to avoid (or improve) re-rendering into functional components?
React.memo
In question the React.memo
. It can be used to prevent re-rendering based on props received. If its functional component using React.memo
also use useState
, useReducer
or useContext
, it will continue to render in the state or context changes.
function MyComponent(props) {
// ...
}
function areEqual(prevProps, nextProps) {
// Retorne `true` se `nextProps` retornaria o mesmo resultado a ser renderizado
// que `prevProps`, senão retorne `false`
}
export default React.memo(MyComponent, areEqual);
useCallback
It is possible to use the hook useCallback
to maintain the reference of a function, since a new reference is created at each render. The useCallback
returns a callback memoized. When is this useful? Well, when this function is in the dependency array another hook.
It also ends up serving the standard comparison of props performed by React, since it will realize that it is the same function. But, if you use useCallback
thinking about it, it might be simpler to use the React.memo
in the child component and compare only the necessary properties, depending on your needs. If this function really is changeable, then the useCallback
can make sense.
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useMemo
The useMemo
returns a memoized value. The documentation recommends using this hook for computationally expensive calculations. Note that the code will be executed during rendering, so if it is something very time consuming, the screen will be locked and you should resolve it in another way.
If you have an expensive calculation that changes only once in a while, according to variables (it also has an array of dependencies), or that is always the same value, the useMemo
may be a solution to make re-rendering cheaper. In this case, the useMemo
will avoid new re-rendering, has nothing to do with it, will only make them cheaper.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Note: In the future, React may choose to "forget" some memoized values and recalculate them in the next render to, for example, free up memory of components that are not on screen.
I gave a more detailed description of the useCallback
and useMemo
in that reply, if you want more details about.
useRef
The statement of constants is a concern raised in the question. It is possible to use the useMemo
to keep the reference of an object or array, but there are options that may be more suitable for this, as pointed out in this article.
Since you do not want to memoize a value in this case, but rather keep the reference, you can make use of the useRef
. The useRef
is like a "box" that stores a mutable value in the property .current
.
Even, is in the FAQ the answer to your question as to whether there is anything in functional components such as declaring constants in classes, in free translation:
The "ref" object is a generic container whose current property is mutable and can contain any value, similar to an instance property in a class.
Example:
function Bla() {
// Inadequado:
// const baz = useMemo(() => [1, 2, 3], [])
// Adequado:
const { current: baz } = useRef([1, 2, 3])
return <Foo baz={baz} />
}
It’s not so simple to improve performance. You need to understand what is getting in the way before you try to fix it, or you may end up getting worse. Not every virtual DOM update causes a real DOM re-rendering.
But, when modifying something, I believe that this answer has addressed well the points for functional components with the three mentioned Hooks (useCallback
, useMemo
and useRef
) and the HOC React.memo
.
One of the ways is
useMemo
anduseCallback
, but there’s something else too. Related: What is the difference between React’s useMemo and useCallback?– Luiz Felipe