Состояние, пропсы и контекст
Подтемы:
Что такое “состояние” (state) компонента в React?
Состояние хранит данные компонента между рендерами. В классовом компоненте state является объектом; значение состояния, созданного useState, может иметь любой тип.
В классовом компоненте состояние задают в constructor или поле state и обновляют через setState. В функциональном используют useState или useReducer. Обновление ставится в очередь; новое значение доступно следующему рендеру.
Пример:
import React, { Component } from "react";
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
count: 0, // Начальное состояние
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Счетчик: {this.state.count}</p>
<button onClick={this.increment}>Увеличить</button>
</div>
);
}
}
export default Counter;
В этом примере компонент Counter имеет состояние count, которое обновляется при клике на кнопку. При вызове setState(), React обновит состояние и перерендерит компонент, отображая новое значение count.
Ссылки для изучения
Как обновить состояние компонента?
В классовом компоненте setState принимает объект обновлений или функцию от предыдущего состояния и поверхностно объединяет результат со state. В функциональном компоненте setter из useState принимает следующее значение или функцию от предыдущего значения и заменяет его целиком. Состояние нельзя изменять напрямую.
Пример использования setState():
import React, { Component } from "react";
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
increment = () => {
// Обновление состояния с использованием объекта
this.setState({ count: this.state.count + 1 });
};
decrement = () => {
// Обновление состояния с использованием функции
this.setState((prevState) => {
return { count: prevState.count - 1 };
});
};
render() {
return (
<div>
<p>Счетчик: {this.state.count}</p>
<button onClick={this.increment}>Увеличить</button>
<button onClick={this.decrement}>Уменьшить</button>
</div>
);
}
}
export default Counter;
В данном примере increment() и decrement() обновляют состояние count с помощью setState(). Важно использовать функции при обновлении состояния, особенно если новое состояние зависит от предыдущего состояния, чтобы избежать проблем с асинхронностью.
Ссылки для изучения
Что такое пропсы (props) в React?
“Пропсы” (props) в React - это механизм передачи данных от родительского компонента к дочернему. Они представляют собой атрибуты, которые задаются при создании компонента и не могут быть изменены самим компонентом, который их получает. Пропсы используются для передачи информации о состоянии или конфигурации компонента.
Пример использования пропсов:
import React from "react";
function Welcome(props) {
return <h1>Привет, {props.name}!</h1>;
}
const App = () => {
return <Welcome name="Алиса" />;
};
export default App;
В этом примере компонент Welcome принимает пропс name и использует его для вывода персонализированного приветствия. Компонент App передает значение “Алиса” в пропс name компонента Welcome.
Пропсы передаются в виде объекта и доступны как свойства (props) внутри дочернего компонента.
Использование пропсов позволяет создавать более гибкие и переиспользуемые компоненты, так как можно настраивать их поведение и отображение извне.
Ссылки для изучения
В чем разница между состоянием и пропсами?
Состояние (state) и пропсы (props) - это два основных концепта в React, используемые для управления данными в компонентах. Они имеют разные цели и характеристики:
state
):
-
Состояние - это внутренние данные компонента, которые могут изменяться во время выполнения.
-
Определяется и управляется самим компонентом, в котором оно находится.
-
Изменение состояния вызывает перерендеринг компонента, чтобы отобразить новое состояние.
-
Доступно только для компонента, в котором было определено состояние.
-
Обновляется через setState в классе, setter из useState или dispatch из useReducer в функциональном компоненте.
props
):
-
Пропсы - это данные, передаваемые от родительского компонента к дочернему компоненту.
-
Нельзя изменить пропсы внутри дочернего компонента. Они считаются “только для чтения”.
-
Пропсы служат для настройки и передачи данных в компоненты.
-
Используются для связи между различными компонентами и передачи информации вниз по иерархии.
-
При рендере родителя дочерний компонент обычно рендерится с новыми пропсами. memo может пропустить рендер, если пропсы не изменились.
Вкратце, состояние предназначено для хранения и управления изменяющимися данными внутри компонента, тогда как пропсы предназначены для передачи данных от родительского компонента к дочернему. Оба этих концепта помогают создавать динамичные и переиспользуемые интерфейсы в React приложениях.
Ссылки для изучения
Как обновить состояние компонента после изменения props?
В React существует несколько способов обновления состояния компонента после изменения его props. Это может потребоваться, когда компонент зависит от входящих props и должен реагировать на их изменения. Вот несколько подходов:
-
componentDidUpdate(prevProps): МетодcomponentDidUpdate()вызывается после обновления компонента. Вы можете сравнить предыдущие и текущие props с помощью параметраprevPropsиthis.props, и при необходимости обновить состояние:componentDidUpdate(prevProps) { if (this.props.someProp !== prevProps.someProp) { this.setState({ someState: newValue }); } } -
getDerivedStateFromProps(nextProps, prevState): Этот статический метод вызывается перед рендерингом и каждый раз, когда происходит обновление props. Вы можете вернуть новое состояние на основе новых props:static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.someProp !== prevState.prevPropValue) { return { someState: newValue }; } return null; } -
useStateсuseEffect: Если вы используете функциональные компоненты, вы можете использоватьuseStateдля хранения состояния иuseEffectдля реагирования на изменения props:import React, { useState, useEffect } from "react"; function MyComponent(props) { const [state, setState] = useState(initialState); useEffect(() => { if (props.someProp !== state.someState) { setState({ someState: newValue }); } }, [props.someProp]); //... }
Не копируйте props в state без необходимости: вычисляйте производные значения во время рендера. Для полного сброса состояния можно изменить key компонента. getDerivedStateFromProps нужен лишь в редких случаях; Effect предназначен прежде всего для синхронизации с внешними системами. Примеры выше схематичны: initialState и newValue должны быть определены, а отслеживаемое предыдущее значение пропса нужно обновлять вместе с состоянием.
Ссылки для изучения
Как передать данные между компонентами вверх и вниз по иерархии?
В React данные могут передаваться между компонентами вверх и вниз по иерархии с использованием пропсов (props) и колбэков (callback функций). Вот как это работает:
Передача данных вниз по иерархии (от родителя к дочернему компоненту):
- Пропсы (props): Родительский компонент передает данные своему дочернему компоненту через пропсы. Дочерний компонент может получить доступ к этим данным как свойства (props).
Пример:
// Родительский компонент
import React from "react";
import ChildComponent from "./ChildComponent";
const ParentComponent = () => {
const data = "Данные для дочернего компонента";
return <ChildComponent dataProp={data} />;
};
// Дочерний компонент
import React from "react";
const ChildComponent = (props) => {
return <p>{props.dataProp}</p>;
};
export default ChildComponent;
Передача данных вверх по иерархии (от дочернего компонента к родителю):
- Колбэки (callback функции): Родительский компонент передает функцию в качестве пропса дочернему компоненту. Дочерний компонент может вызвать эту функцию, передавая ей данные обратно вверх по иерархии.
Пример:
// Родительский компонент
import React, { useState } from "react";
import ChildComponent from "./ChildComponent";
const ParentComponent = () => {
const [receivedData, setReceivedData] = useState("");
const handleDataChange = (data) => {
setReceivedData(data);
};
return (
<div>
<p>Полученные данные: {receivedData}</p>
<ChildComponent onDataChange={handleDataChange} />
</div>
);
};
// Дочерний компонент
import React from "react";
const ChildComponent = (props) => {
const sendDataToParent = () => {
props.onDataChange("Данные от дочернего компонента");
};
return <button onClick={sendDataToParent}>Отправить данные</button>;
};
export default ChildComponent;
Это позволяет дочернему компоненту воздействовать на данные и состояние родительского компонента.
Используя этот подход, вы можете эффективно передавать и обновлять данные между компонентами вверх и вниз по иерархии.
Ссылки для изучения
Что такое “подъем состояния” (lifting state up)?
“Подъем состояния” (lifting state up) - это паттерн в React, который предполагает перемещение состояния из дочерних компонентов в родительские компоненты, чтобы разделять и управлять состоянием на более высоком уровне и передавать его через пропсы.
Этот подход особенно полезен, когда несколько компонентов должны иметь доступ к одному и тому же состоянию или когда состояние должно быть синхронизировано между разными частями приложения.
Пример с подъемом состояния:
import React, { useState } from "react";
const TemperatureInput = ({ scale, temperature, onTemperatureChange }) => {
return (
<fieldset>
<legend>Введите температуру в градусах {scale}:</legend>
<input
value={temperature}
onChange={(e) => onTemperatureChange(e.target.value)}
/>
</fieldset>
);
};
const Calculator = () => {
const [celsius, setCelsius] = useState("");
const [fahrenheit, setFahrenheit] = useState("");
const handleCelsiusChange = (value) => {
setCelsius(value);
setFahrenheit((value * 9) / 5 + 32);
};
const handleFahrenheitChange = (value) => {
setFahrenheit(value);
setCelsius(((value - 32) * 5) / 9);
};
return (
<div>
<TemperatureInput
scale="C"
temperature={celsius}
onTemperatureChange={handleCelsiusChange}
/>
<TemperatureInput
scale="F"
temperature={fahrenheit}
onTemperatureChange={handleFahrenheitChange}
/>
</div>
);
};
export default Calculator;
В этом примере компонент Calculator поднимает состояние температур из TemperatureInput компонентов. При изменении температуры в одном из вводов, состояние поднимается в родительский компонент Calculator, который затем обновляет состояние для другого ввода, обеспечивая синхронизацию температур в обеих шкалах.
Ссылки для изучения
Что такое “нормализация состояния” (state normalization) в контексте управления состоянием приложения?
Нормализация состояния (state normalization) - это паттерн управления состоянием, который используется для организации данных в хранилище таким образом, чтобы обеспечить эффективное хранение, обновление и доступ к данным в Redux или других системах управления состоянием.
Основная идея нормализации состоит в том, чтобы хранить данные в нормализованной форме, где каждый тип данных хранится в отдельной коллекции (таблице), и используются ссылки между различными коллекциями для устранения дублирования данных и облегчения обновления.
Пример: Предположим, у нас есть список постов и список комментариев к этим постам. Вместо хранения комментариев внутри каждого поста (что может привести к дублированию данных), мы можем создать две коллекции: “посты” и “комментарии”, и использовать идентификаторы постов в комментариях для установления связи между ними.
Преимущества нормализации состояния:
-
Экономия места: Дублирование данных уменьшается, так как одни и те же данные не хранятся в нескольких местах.
-
Легкость обновления: Изменение данных в одной коллекции автоматически отражается в других, где эти данные используются.
-
Быстрый доступ: Поиск, фильтрация и доступ к данным ускоряются, так как данные разделены на более мелкие коллекции.
-
Прозрачность: Структура хранилища становится более легко понимаемой и отслеживаемой.
В то время как нормализация может помочь в оптимизации и управлении состоянием, она также добавляет сложности в логику обновления данных и может потребовать дополнительной работы при выборе данных для отображения в компонентах. Вам стоит выбрать подход в зависимости от размера и сложности вашего приложения.
Ссылки для изучения
Примеры хороших ответов из реальных собеседований
- Два реальных технических собеседования на Middle/Senior Frontend-разработчика · 52:31–53:52Техническое собеседование · Ответ кандидата
Кандидат объясняет хранение сущностей по идентификатору вместо массива и сравнивает доступ по ключу с поиском через find.
Как управлять состоянием между компонентами, не имеющими близкой связи?
Существует несколько методов для управления состоянием между компонентами, которые не имеют прямой связи. Вот некоторые из них:
-
Поднятие состояния (
Lifting State Up): Вы можете поднять состояние до ближайшего общего родительского компонента, который имеет доступ и к одному, и к другому компоненту. Затем передавайте это состояние как пропсы в каждый компонент. Этот подход особенно полезен, когда компоненты имеют общий родительский компонент, но не имеют прямой связи друг с другом. -
Контекст (
Context API): Используйте Context API, который позволяет передавать данные глубоко в дерево компонентов без явной передачи пропсов через все уровни. Создайте контекст и оберните ваше дерево компонентов этим контекстом. Таким образом, компоненты смогут получать доступ к данным из контекста, даже если они не находятся в прямой иерархии. -
Глобальное состояние (
Global State): Используйте библиотеки управления состоянием, такие как Redux или MobX, чтобы создать глобальное хранилище данных, доступное для всех компонентов. Это особенно полезно, если состояние нужно между компонентами, которые находятся далеко друг от друга в дереве компонентов. -
Событийная система: Реализуйте свою событийную систему, которая позволит компонентам подписываться на определенные события и передавать данные. Это можно сделать с помощью сторонних библиотек или самостоятельно.
-
RESTful API: Если ваши компоненты общаются с сервером, вы можете использовать RESTful API для обмена данными между ними. Компоненты могут получать и отправлять данные через API.
-
Библиотеки для управления состоянием: В зависимости от вашего опыта и требований, вы также можете рассмотреть использование библиотек управления состоянием, таких как Recoil, Zustand и других.
Выбор метода будет зависеть от конкретных требований вашего приложения, его архитектуры и сложности взаимодействия между компонентами.
Ссылки для изучения
Какие популярные библиотеки для управления состоянием вы знаете, помимо встроенного useState?
Популярные библиотеки для управления состоянием в React:
-
Redux:-
Централизованное хранилище состояния с предсказуемым поведением.
-
Пример использования:
import { createStore } from 'redux'; import { Provider } from 'react-redux';
-
-
MobX:-
Реактивное управление состоянием с фокусом на наблюдаемых данных и реакциях.
-
Пример использования:
import { observable } from 'mobx'; import { observer } from 'mobx-react';
-
-
Recoil:-
Recoil использует атомы и селекторы, но его репозиторий архивирован в январе 2025 года. Это исторический пример, а не рекомендация для нового проекта.
-
Пример использования:
import { RecoilRoot, atom, useRecoilState } from 'recoil';
-
-
Zustand:-
Легковесная и гибкая библиотека для управления состоянием.
-
Пример использования:
import { create } from 'zustand';
-
-
Context API:-
Встроенный механизм для управления глобальным состоянием с помощью контекста.
-
Пример использования:
import { createContext, useContext } from 'react';
-
Помимо встроенного useState, для управления состоянием в React популярны библиотеки Redux, MobX, Recoil, Zustand, а также встроенный Context API.
Ссылки для изучения
Примеры хороших ответов из реальных собеседований
- Frontend собеседование на 400.000₽ в СБЕР: Опытный ИНТЕРВЬЮЕР про FSD и джунов · 42:54–44:30Техническое собеседование · Ответ кандидата
Кандидат называет Redux, Zustand, React Query и RTK Query и разделяет клиентское состояние и кэш серверных данных.
Какие паттерны использования Redux вы знаете?
Паттерны использования Redux:
-
Действия (
Actions):-
Описывают изменения состояния через типы и полезную нагрузку.
-
Пример:
const increment = () => ({ type: 'INCREMENT' });
-
-
Редьюсеры (
Reducers):-
Функции, которые описывают, как состояние изменяется в ответ на действия.
-
Пример:
const counter = (state = 0, action) => { switch (action.type) { case 'INCREMENT': return state + 1; default: return state; } };
-
-
Хранилище (
Store):-
Хранит состояние приложения и предоставляет методы для доступа к состоянию и отправки действий.
-
Пример:
import { createStore } from 'redux'; const store = createStore(counter);
-
-
Контейнерные компоненты (
Container Components):-
Соединяют компоненты React с Redux-хранилищем с помощью
connect. -
Пример:
import { connect } from 'react-redux'; const mapStateToProps = (state) => ({ count: state }); const mapDispatchToProps = { increment }; export default connect(mapStateToProps, mapDispatchToProps)(CounterComponent);
-
-
Thunk Middleware:-
Позволяет писать действия, которые возвращают функции вместо объектов, для работы с асинхронными операциями.
-
Пример:
import { thunk } from 'redux-thunk'; import { applyMiddleware, createStore } from 'redux'; const store = createStore(rootReducer, applyMiddleware(thunk));
-
-
Селекторы (
Selectors):-
Функции для выборки данных из состояния, повышают читаемость и повторное использование кода.
-
Пример:
const getCount = (state) => state.counter;
-
Популярные паттерны использования Redux включают действия, редьюсеры, хранилище, контейнерные компоненты, thunk middleware и селекторы. Эти паттерны помогают структурировать и управлять состоянием приложения эффективно.
Ссылки для изучения
Какие альтернативы Redux для управления состоянием в React-приложениях?
Альтернативы Redux для управления состоянием в React-приложениях:
-
MobX:-
Реактивное управление состоянием с акцентом на наблюдаемые данные и автоматические реакции.
-
Пример:
import { observable, action } from 'mobx'; import { observer } from 'mobx-react';
-
-
Recoil:-
Recoil использует атомы и селекторы, но его репозиторий архивирован в январе 2025 года. Это исторический пример, а не рекомендация для нового проекта.
-
Пример:
import { RecoilRoot, atom, useRecoilState } from 'recoil';
-
-
Zustand:-
Легковесная и гибкая библиотека для управления состоянием.
-
Пример:
import { create } from 'zustand'; const useStore = create(set => ({ count: 0, increment: () => set(state => ({ count: state.count + 1 })) }));
-
-
Context API:-
Встроенный механизм для управления глобальным состоянием с помощью контекста.
-
Пример:
import { createContext, useContext, useState } from 'react'; const MyContext = createContext(); const MyProvider = ({ children }) => { const [state, setState] = useState(0); return <MyContext.Provider value={{ state, setState }}>{children}</MyContext.Provider>; };
-
-
XState:-
Управление состоянием с использованием конечных автоматов и диаграмм состояний.
-
Схематичный пример старого API XState 4. В XState 5 используют createMachine или setup; useMachine вызывают внутри компонента или пользовательского хука:
import { Machine } from 'xstate'; import { useMachine } from '@xstate/react'; const toggleMachine = Machine({ /* состояние машины */ }); const [state, send] = useMachine(toggleMachine);
-
-
React Query:-
Специализируется на управлении состоянием данных, извлекаемых из серверов.
-
Пример для React Query 3; useQuery вызывается внутри компонента или пользовательского хука. В TanStack Query 5 используется объект параметров:
import { useQuery } from 'react-query'; const { data, error, isLoading } = useQuery('data', fetchData);
-
Альтернативы Redux для управления состоянием в React-приложениях включают MobX, Recoil, Zustand, Context API, XState и React Query. Эти библиотеки и инструменты предлагают разные подходы и функциональные возможности для управления состоянием.
Ссылки для изучения
Когда useReducer лучше useState?
useReducer удобнее, когда состояние меняется через несколько связанных переходов, следующие значения зависят от предыдущих, а правила обновления разбросались по обработчикам. Reducer принимает state и action и возвращает новое состояние, поэтому переходы можно читать и тестировать как чистую функцию. Это полезно для сложной формы, wizard или state machine. Для одного независимого boolean или строки useState проще. Reducer не делает состояние глобальным и не уменьшает render автоматически; его ценность состоит в явной модели переходов. Побочные эффекты выполняют вне reducer.
Ссылки для изучения
По каким признакам приложению нужен внешний стейт-менеджер?
Внешний state manager оправдан, если одно клиентское состояние читают и изменяют далёкие части дерева, нужны сложные переходы, middleware, time-travel или единая диагностика. До выбора отделяют server state: запросы, кеш, дедупликацию и инвалидирование лучше часто решает специализированная библиотека. Локальные поля формы, hover и открытие модалки оставляют рядом с компонентом. Context подходит редко меняющимся зависимостям, но частые широкие обновления требуют аккуратного разделения. Решение принимают по владельцу данных, частоте обновлений и реальным проблемам отладки. Дополнительная инфраструктура должна окупать сериализацию, boilerplate и обучение команды.
Ссылки для изучения
Что такое контекст (context) в React и для чего он используется?
Контекст (
Context
) — это механизм, который позволяет передавать данные через
дерево компонентов без необходимости передавать пропсы на каждом уровне.
Основные характеристики:
-
Обмен данными между компонентами:
- Используется для обмена данными между компонентами, которые находятся на разных уровнях вложенности.
-
Создание и использование контекста:
-
Создается с помощью
React.createContext. -
Обеспечивает доступ к данным через компоненты
ProviderиConsumer.
-
Пример:
import React, { createContext, useContext } from "react";
const MyContext = createContext();
const MyProvider = ({ children }) => {
const value = "Hello, World!";
return <MyContext.Provider value={value}>{children}</MyContext.Provider>;
};
const MyComponent = () => {
const value = useContext(MyContext);
return <div>{value}</div>;
};
const App = () => (
<MyProvider>
<MyComponent />
</MyProvider>
);
export default App;
Контекст в React используется для передачи данных через дерево компонентов, избегая необходимости прокидывать пропсы на каждом уровне.
Ссылки для изучения
Примеры хороших ответов из реальных собеседований
- SENIOR FRONTEND самое полное МОК-ИНТЕРВЬЮ + LIVE CODING / Разбор 100 вопросов · 1:23:22–1:24:36Мок-собеседование · Ответ кандидата
На учебном интервью объясняют, как Context избавляет от передачи темы через каждый промежуточный компонент и как useContext получает общее значение.
Как работает механизм “контекста” (context) для передачи данных?
Контекст (
Context
) — это механизм, который позволяет передавать данные через
дерево компонентов без необходимости передавать пропсы на каждом уровне.
Основные шаги:
-
Создание контекста:
- Создайте контекст с помощью
React.createContext.
const MyContext = React.createContext(); - Создайте контекст с помощью
-
Обертка компонентов в Provider:
- Используйте компонент
Providerдля обертки тех компонентов, которые должны иметь доступ к контексту. Укажите значение, которое должно быть передано через контекст.
<MyContext.Provider value={/* значение */}> {/* дочерние компоненты */} </MyContext.Provider> - Используйте компонент
-
Потребление контекста:
- Компоненты, которые должны получать данные из контекста, могут использовать компонент
Consumerили хукuseContext.
// Использование хука useContext const value = useContext(MyContext); - Компоненты, которые должны получать данные из контекста, могут использовать компонент
Пример:
import React, { createContext, useContext } from "react";
// Создание контекста
const MyContext = createContext();
// Компонент Provider
const MyProvider = ({ children }) => {
const value = "Hello, World!";
return <MyContext.Provider value={value}>{children}</MyContext.Provider>;
};
// Компонент Consumer с хуком useContext
const MyComponent = () => {
const value = useContext(MyContext);
return <div>{value}</div>;
};
// Основное приложение
const App = () => (
<MyProvider>
<MyComponent />
</MyProvider>
);
export default App;
Контекст в React позволяет передавать данные через дерево компонентов с помощью компонентов Provider и Consumer, а также хука useContext, упрощая обмен данными между компонентами на разных уровнях вложенности.
Ссылки для изучения
Что такое “передача данных через контекст” (context passing) в React?
Передача данных через контекст — это механизм, который позволяет передавать данные напрямую через дерево компонентов без необходимости передавать пропсы на каждом уровне.
Основные шаги:
-
Создание контекста:
- Создайте контекст с помощью
React.createContext.
const MyContext = React.createContext(); - Создайте контекст с помощью
-
Использование Provider:
- Оберните дерево компонентов в
MyContext.Providerи передайте значение контекста.
<MyContext.Provider value={/* значение */}> {/* дочерние компоненты */} </MyContext.Provider> - Оберните дерево компонентов в
-
Использование Consumer или useContext:
- Компоненты, которые должны получать данные из контекста, используют
MyContext.Consumerили хукuseContext.
const value = useContext(MyContext); - Компоненты, которые должны получать данные из контекста, используют
Пример:
import React, { createContext, useContext } from "react";
// Создание контекста
const MyContext = createContext();
// Компонент Provider
const MyProvider = ({ children }) => {
const value = "Hello, World!";
return <MyContext.Provider value={value}>{children}</MyContext.Provider>;
};
// Компонент Consumer с хуком useContext
const MyComponent = () => {
const value = useContext(MyContext);
return <div>{value}</div>;
};
// Основное приложение
const App = () => (
<MyProvider>
<MyComponent />
</MyProvider>
);
export default App;
Передача данных через контекст позволяет эффективно передавать данные через дерево компонентов, избегая необходимости пропс-дриллинга (передачи пропсов на каждом уровне).
Ссылки для изучения
Примеры хороших ответов из реальных собеседований
- SENIOR FRONTEND самое полное МОК-ИНТЕРВЬЮ + LIVE CODING / Разбор 100 вопросов · 1:23:22–1:24:36Мок-собеседование · Ответ кандидата
На учебном интервью объясняют передачу общей темы через Context без промежуточных props и получение значения через useContext.







