React.memo() - ваш друг
React.memo() - одна из тех вещей, которые должны быть частью арсенала каждого разработчика React. Она дает нам возможность запоминать компонент React. Как и в случае с любым другим инструментом, прежде чем мы углубимся в то, как использовать React.memo(), давайте сначала разберемся с проблемой.
Зачем нужна мемоизация?
Мемоизация - это общая концепция, которая в основном означает кеширование результатов каких-либо вычислений для последующего использования. Это метод оптимизации, который довольно широко используется в мире программирования.
Важно помнить, что всякий раз, когда используется мемоизация, должен быть критерий, который будет определять, когда кешированные результаты больше не действительны, и вычисление должно быть выполнено снова.
Чтобы понять проблему, которую это решает, рассмотрим следующий компонент React:
import { useState, Fragment } from "react";
function App() {
const [count, setCount] = useState(0);
function handleDecrement() {
setCount((oldCount) => --oldCount);
}
function handleIncrement() {
setCount((oldCount) => ++oldCount);
}
return (
<Fragment>
<p>Count is {count}</p>
<button onClick={handleDecrement}>-</button>
<button onClick={handleIncrement}>+</button>
</Fragment>
);
}
export default App;
Простой компонент, который ведет счет, который можно увеличивать или уменьшать.
Теперь добавим еще один компонент в <App />. Чтобы упростить задачу, мы создадим компонент <Message />, который возвращает какое-либо сообщение в зависимости от идентификатора msgId, переданного ему в качестве реквизита.
function Message(props) {
let msg = "hello, world";
if (props.msgId === 1) {
msg = "hey there!";
} else if (props.msgId === 2) {
msg = "hola!";
}
return <p>{msg}</p>;
}
Мы сохранили здесь простоту, но представьте, что этот компонент <Message /> выполняет тяжелые вычисления или, возможно, отправляет запрос во внешний API, чтобы получить окончательное сообщение. Мы смоделируем эту ситуацию, добавив console.log() в микс всех фаворитов.
function Message(props) {
let msg = "hello, world";
console.log("Just performed some seriously heavy computation");
if (props.msgId === 1) {
msg = "hey there!";
} else if (props.msgId === 2) {
msg = "hola!";
}
return <p>{msg}</p>;
}
Давайте обновим компонент <App /> чтобы использовать <Message />.
import { useState, Fragment } from "react";
function Message(props) {
let msg = "hello, world";
console.log("Just performed some seriously heavy computation");
if (props.msgId === 1) {
msg = "hey there!";
} else if (props.msgId === 2) {
msg = "hola!";
}
return <p>{msg}</p>;
}
function App() {
const [count, setCount] = useState(0);
function handleDecrement() {
setCount((oldCount) => --oldCount);
}
function handleIncrement() {
setCount((oldCount) => ++oldCount);
}
return (
<Fragment>
<Message msgId={1} />
<p>Count is {count}</p>
<button onClick={handleDecrement}>-</button>
<button onClick={handleIncrement}>+</button>
</Fragment>
);
}
export default App;
В видео ниже обратите особое внимание на тот факт, что при каждом изменении count выполняются тяжелые вычисления.
На этом этапе сделайте шаг назад и подумайте, насколько неэффективен наш пользовательский интерфейс в данный момент. count никак не влияет на <Message />, но все же каждый раз при обновлении count выполняется очень тяжелое вычисление. Мы хотим, чтобы вычисление происходило только при изменении msgId, потому что изменение msgId должно привести к другому сообщению.
React.memo () спешит на помощь
React.memo() компонент более высокого порядка. Он принимает компонент в качестве аргумента и запоминает результат. Мемоизированный результат обновляется только в случае изменения свойств исходного компонента.
Чтобы использовать React.memo(), просто передайте свой компонент в качестве аргумента и сохраните результат. Нашим компонентом <Message /> станет:
import { useState, Fragment, memo } from "react";
const Message = memo(function (props) {
let msg = "hello, world";
console.log("Just performed some seriously heavy computation");
if (props.msgId === 1) {
msg = "hey there!";
} else if (props.msgId === 2) {
msg = "hola!";
}
return <p>{msg}</p>;
});
Примечание: я только импортировал сюда memo(). Если вы импортировали React, вы можете использовать вместо React.memo() просто memo().
Теперь наш код выглядит так:
import { useState, Fragment, memo } from "react";
const Message = memo(function (props) {
let msg = "hello, world";
console.log("Just performed some seriously heavy computation");
if (props.msgId === 1) {
msg = "hey there!";
} else if (props.msgId === 2) {
msg = "hola!";
}
return <p>{msg}</p>;
});
function App() {
const [count, setCount] = useState(0);
function handleDecrement() {
setCount((oldCount) => --oldCount);
}
function handleIncrement() {
setCount((oldCount) => ++oldCount);
}
return (
<Fragment>
<Message msgId={1} />
<p>Count is {count}</p>
<button onClick={handleDecrement}>-</button>
<button onClick={handleIncrement}>+</button>
</Fragment>
);
}
export default App;
На этот раз обратите внимание, что вычисления выполняются, когда приложение обновляется, но изменение count больше не приводит к такому результату.