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
больше не приводит к такому результату.