Когда не следует использовать React memo?
Я недавно играл с React 16.6.0
и мне нравится идея React Memo, но я не смог найти ничего относительно сценариев, наиболее подходящих для его реализации.
Документы React (https://reactjs.org/docs/react-api.html#reactmemo), по-видимому, не предполагают каких-либо последствий от использования их на всех ваших функциональных компонентах.
Поскольку он выполняет поверхностное сравнение, чтобы выяснить, нужно ли выполнить повторную визуализацию, возникнет ли когда-нибудь ситуация, которая негативно скажется на производительности?
Такая ситуация кажется очевидным выбором для реализации:
// NameComponent.js
import React from "react";
const NameComponent = ({ name }) => <div>{name}</div>;
export default React.memo(NameComponent);
// CountComponent.js
import React from "react";
const CountComponent = ({ count }) => <div>{count}</div>;
export default CountComponent;
// App.js
import React from "react";
import NameComponent from "./NameComponent";
import CountComponent from "./CountComponent";
class App extends Component {
state = {
name: "Keith",
count: 0
};
handleClick = e => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<NameComponent name={this.state.name} />
<CountComponent count={this.state.count} />
<button onClick={this.handleClick}>Add Count</button>
</div>
);
}
}
Поскольку name
никогда не изменится в этом контексте, имеет смысл запомнить.
Но как насчет ситуации, когда реквизит часто меняется?
Что, если бы я добавил еще одну кнопку, которая изменила что-то еще в состоянии и запустила повторный рендеринг, имеет ли смысл обернуть CountComponent
в памятку, даже если этот компонент по замыслу предназначен для частого обновления?
Я предполагаю, что мой главный вопрос - пока все остается чистым, была ли когда-нибудь ситуация, чтобы не обернуть функциональный компонент React Memo?
Ответы
Ответ 1
Все реагирующие компоненты реализуют метод shouldComponentUpdate()
. По умолчанию (компоненты, расширяющие React.Component
), это всегда возвращает true. Изменение, которое вносит в память компонент (через React.memo
для функциональных компонентов или расширение React.PureComponent
для компонентов класса), представляет собой реализацию метода shouldComponentUpdate()
который выполняет поверхностное сравнение состояния и реквизитов.
Если посмотреть на документацию по методам жизненного цикла компонента, shouldComponentUpdate()
всегда вызывается до того, как происходит рендеринг, это означает, что запоминание компонента будет включать это дополнительное поверхностное сравнение при каждом обновлении.
Принимая это во внимание, запоминание компонента действительно влияет на производительность, и величина этих эффектов должна быть определена путем профилирования вашего приложения и определения того, работает ли он лучше с запоминанием или без него.
Чтобы ответить на ваш вопрос, я не думаю, что существует явное правило, когда вы должны или не должны запоминать компоненты, однако я думаю, что должен применяться тот же принцип, что и при принятии решения о том, следует ли вам переопределять shouldComponentUpdate()
: находить проблемы с производительностью через Предлагаемые инструменты профилирования и определить, нужно ли оптимизировать компонент.
Ответ 2
"Помните, что функция, переданная useMemo, запускается во время рендеринга. Не делайте там ничего, что вы обычно не делаете во время рендеринга. Например, побочные эффекты принадлежат useEffect, а не useMemo.
Вы можете использовать useMemo как оптимизацию производительности, а не как семантическую гарантию. В будущем React может решить "забыть" некоторые ранее запомненные значения и пересчитать их при следующем рендеринге, например, чтобы освободить память для компонентов вне экрана. Напишите свой код, чтобы он по-прежнему работал без useMemo, а затем добавьте его для оптимизации производительности. (В редких случаях, когда значение никогда не нужно пересчитывать, вы можете лениво инициализировать ссылку.) "
https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies
Ответ 3
Я думаю, что краткий ответ: React.memo делает с функциональными компонентами то же, что React.PureComponent делает с компонентами класса. В этом смысле, когда вы используете memo, он будет оценивать, изменились ли реквизиты для этого функционального компонента, если это так, то он выполнит возврат функции, в противном случае он не будет, избегая повторного рендеринга компонента.
import React, { memo } from 'react';
const Component = () => {
debugger;
return (
<div>Hello World!</div>
);
};
const MemoComponent = memo(() => {
debugger;
return (
<div>Hello World!</div>
);
});