В каких случаях компонент будет перерисовываться
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
В каких случаях компонент будет перерисовываться
Перерисовка (re-render) компонента в React происходит когда нужно обновить DOM на основе изменений в состоянии, пропсах или контексте. Понимание причин перерисовки критично для оптимизации производительности приложения.
Основные причины перерисовки
1. Изменение state
Это самая частая причина:
function Counter() {
const [count, setCount] = useState(0);
// Компонент перерисуется когда setCount вызовет изменение count
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
Каждый вызов setCount с НОВЫМ значением = перерисовка.
2. Изменение props
function Child({ name }) {
console.log('Child перерисовалась');
return <div>Hello, {name}!</div>;
}
function Parent() {
const [user, setUser] = useState('Alice');
// Каждое изменение user → Child перерисуется
return <Child name={user} />;
}
3. Изменение Context
const ThemeContext = createContext();
function App() {
const [theme, setTheme] = useState('light');
// Все потребители контекста перерисуются когда изменится value
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Header />
<Content />
<Footer />
</ThemeContext.Provider>
);
}
4. Родитель перерисовался
Важный момент: если родитель перерисовался, все его дети также перерисуются по умолчанию!
function Parent() {
const [counter, setCounter] = useState(0);
return (
<div>
<p>Count: {counter}</p>
<button onClick={() => setCounter(counter + 1)}>+1</button>
{/* Child перерисуется ДА ЖЕ если его props не изменились! */}
<Child name="Bob" />
</div>
);
}
function Child({ name }) {
console.log('Child перерисовалась'); // Логирует каждый раз
return <div>{name}</div>;
}
Оптимизация перерисовок
1. React.memo
Предотвращает перерисовку если props не изменились:
const Child = React.memo(({ name }) => {
console.log('Child перерисовалась');
return <div>{name}</div>;
});
function Parent() {
const [counter, setCounter] = useState(0);
return (
<div>
<p>Count: {counter}</p>
<button onClick={() => setCounter(counter + 1)}>+1</button>
{/* Child НЕ будет перерисована при клике */}
<Child name="Bob" />
</div>
);
}
2. useMemo
Мемоизирует вычисленное значение:
function Parent() {
const [counter, setCounter] = useState(0);
const [text, setText] = useState('');
// expensiveValue пересчитывается только когда counter изменяется
const expensiveValue = useMemo(() => {
console.log('Дорогое вычисление');
return counter * 2;
}, [counter]); // зависимость: counter
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<p>Expensive: {expensiveValue}</p>
{/* Child только если expensiveValue изменилась */}
<Child value={expensiveValue} />
</div>
);
}
3. useCallback
Мемоизирует функцию для передачи в пропсы:
const ChildWithButton = React.memo(({ onClick }) => {
console.log('ChildWithButton перерисовалась');
return <button onClick={onClick}>Click</button>;
});
function Parent() {
const [counter, setCounter] = useState(0);
// ❌ Без useCallback — функция пересоздаётся, Child перерисуется
const handleClick = () => console.log(counter);
// ✅ С useCallback — функция мемоизирована
const handleClick = useCallback(() => {
console.log(counter);
}, [counter]); // зависимость: counter
return (
<div>
<p>Count: {counter}</p>
<button onClick={() => setCounter(counter + 1)}>+1</button>
{/* Child не перерисуется если counter не в зависимостях */}
<ChildWithButton onClick={handleClick} />
</div>
);
}
4. Правильная структура state
// ❌ Плохо: единый большой state вызывает перерисовку всего
function App() {
const [data, setData] = useState({ user: {}, posts: [], settings: {} });
return (
<>
<User data={data.user} />
<Posts data={data.posts} />
<Settings data={data.settings} />
</>
);
}
// ✅ Хорошо: разделить state или использовать Context
function App() {
const [user, setUser] = useState({});
const [posts, setPosts] = useState([]);
const [settings, setSettings] = useState({});
return (
<>
<User data={user} />
<Posts data={posts} />
<Settings data={settings} />
</>
);
}
Пример: Все причины в одном компоненте
function ComplexExample() {
const [count, setCount] = useState(0); // Причина 1: state
const theme = useContext(ThemeContext); // Причина 3: context
// Причина 4: если родитель перерисовался, этот тоже перерисуется
console.log('ComplexExample перерисовалась');
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
{/* Причина 2: Child перерисуется если theme изменится */}
<Child theme={theme} />
</div>
);
}
Как отследить перерисовки
1. Используй console.log
function MyComponent() {
console.log('MyComponent перерисовалась');
return <div>Content</div>;
}
2. DevTools Profiler (React)
- Открыть React DevTools
- Вкладка "Profiler"
- Кнопка "Record"
- Взаимодействовать с приложением
- Видеть какие компоненты перерисовались и почему
3. Highlight updated components
В React DevTools:
- Settings → Highlight updates when components render
- Обновляемые компоненты будут подсвечиваться
Важные правила
✅ DO:
- Используй
useStateдля локального состояния - Мемоизируй дорогие вычисления с
useMemo - Мемоизируй callback функции с
useCallback - Используй
React.memoдля чистых компонентов
❌ DON'T:
- Не создавай новые объекты/массивы в каждом рендере
- Не передавай inline функции как пропсы
- Не используй Context для часто меняющихся данных (используй Redux/Zustand)
Перерисовка в Next.js
В Next.js с Server Components дополнительно:
// Server Component — не перерисуется вообще
export default async function ServerComponent() {
const data = await fetchData();
return <div>{data}</div>;
}
// Client Component — перерисуется когда state/props изменятся
'use client';
export default function ClientComponent() {
const [data, setData] = useState(null);
return <div>{data}</div>;
}
Четыре главных причины перерисовки: изменение state, props, context, или перерисовка родителя. Оптимизация требует понимания этих механизмов!