How to optimize rendering on functional components in React?

Asked

Viewed 72 times

2

In my studies on React Native I’m bumping into rendering optimization on components, specifically on functional components.

It is known that any change in status or props, the default component is rendered, as are all its child components. But sometimes such a change does not imply a necessary visual change, making rendering the components unnecessary.

To get around such a problem we can use PureComponents for class components, and in the case of functional components use the React.memo which has the limitation of checking only the props and not the states, which is already something bad.

The question I have come to raise here is about the method render() which exists in class components. With it we can put all the code that only needs to be executed once (such as the declaration of constants) outside the same and within the scope of the component. This is recommended, as doing this whenever the component is rendered, such constants will not be reloaded unnecessarily, just like any other code that is fixed.

But how is such an approach made into functional components? How do you declare such constants or fixed codes so that they are not unnecessarily reloaded with each rendering of the component, since EVERYTHING that is encoded within the scope of the component is re-loaded? What is the right way to approach this in functional components?

1 answer

2


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.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.