Обычно к memo
прибегают в крайних случаях. Если в приложении есть проблемы с производительностью, то чаще всего это связано с неправильной компоновкой и неудачной работой с состоянием.
В этом уроке мы разберем несколько приемов построения компонентов, которые помогут избежать проблем с оптимизацией не прибегая к memo
.
Пример ниже состоит из двух компонентов, в котором при изменении состояния в одном компоненте отрисовывается дочерний:
import { useState, useEffect } from 'react';
const Greeting = () => {
useEffect(() => {
console.log(`Компонент Greeting отрисован в ${new Date().toLocaleTimeString()}`);
});
return <h3>Hello, world!</h3>;
};
const App = () => {
const [name, setName] = useState('');
return (
<>
<input value={name} onChange={(e) => setName(e.target.value)} />
<Greeting />
</>
);
};
Чтобы избежать лишнего рендера Greeting
, достаточно убрать из App
состояние. Этого можно добиться выделением дополнительного компонента:
import { useState, useEffect } from 'react';
const Form = () => {
const [name, setName] = useState('');
return <input value={name} onChange={(e) => setName(e.target.value)} />
};
const Greeting = () => {
useEffect(() => {
console.log(`Компонент Greeting отрисован в ${new Date().toLocaleTimeString()}`);
});
return <h3>Hello, world!</h3>;
};
const App = () => {
return (
<>
<Form />
<Greeting />
</>
);
};
Может показаться, что это усложнило приложение из-за добавления еще одного компонента. Но на самом деле такой код лучше описывает архитектуру. Состояние теперь выделено там, где используется, нет компонентов, которые бы перерисовывались без необходимости при изменении состояния.
Такой подход не всегда может работать, разберем это на другом примере:
import { useState, useEffect } from 'react';
const Greeting = () => {
useEffect(() => {
console.log(`Компонент Greeting отрисован в ${new Date().toLocaleTimeString()}`);
});
return <h3>Hello, world!</h3>;
};
const App = () => {
const [name, setName] = useState('');
return (
<div data-name={name}>
<input value={name} onChange={(e) => setName(e.target.value)} />
<Greeting />
</div>
);
};
Здесь мы не можем выделить состояние так же, как в предыдущем случае, потому что родительский элемент тоже использует состояние. Так не получится сделать:
<div data-name={name}>
<Form />
<Greeting />
</div>
Однако выход есть, и он достаточно простой:
import { useState, useEffect } from 'react';
const Form = ({ children }) => {
const [name, setName] = useState('');
return (
<div data-name={name}>
<input value={name} onChange={(e) => setName(e.target.value)} />
{children}
</div>
);
};
const Greeting = () => {
useEffect(() => {
console.log(`Компонент Greeting отрисован в ${new Date().toLocaleTimeString()}`);
});
return <h3>Hello, world!</h3>;
};
const App = () => {
return (
<Form>
<Greeting />
</Form>
);
};
Как и прежде, мы добавили еще один компонент Form
, внутри которого используется состояние и находятся компоненты, зависящие от этого состояния. Компоненты, которым это состояние не нужно, остались в корневом компоненте App
и попадают в Form
через children
.
Такой простой способ позволяет изолировать состояние от компонентов, которые не зависят от него. Прежде чем использовать memo
, лучше рассмотреть этот подход. Он более выгоден, так как позволяет выстроить правильную композицию приложения.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.