← Назад к вопросам
Как организован двусторонний обмен информацией между родительским и дочерним компонентами?
1.6 Junior🔥 141 комментариев
#React
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Двусторонний обмен данными между компонентами в React
Двусторонняя коммуникация (parent <-> child) - это один из фундаментальных паттернов в React. Существует несколько способов её реализации, каждый с собственными преимуществами.
1. Props и Callback функции (базовый паттерн)
Это стандартный React паттерн:
- Parent передает данные через props
- Child отправляет данные через callback функцию
// Parent компонент
function Parent() {
const [count, setCount] = useState(0);
const handleChildUpdate = (newValue) => {
setCount(newValue);
};
return (
<div>
<p>Значение: {count}</p>
<Child value={count} onChange={handleChildUpdate} />
</div>
);
}
// Child компонент
function Child({ value, onChange }) {
return (
<div>
<p>Текущее значение: {value}</p>
<button onClick={() => onChange(value + 1)}>
Увеличить
</button>
</div>
);
}
2. Управляемые компоненты (Controlled Components)
Child полностью контролируется Parent. Это лучше всего работает с формами:
// Parent
function Form() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
console.log({ email, password });
};
return (
<form onSubmit={handleSubmit}>
<Input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<Input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Пароль"
/>
<button type="submit">Отправить</button>
</form>
);
}
// Child (Input)
function Input({ type, value, onChange, placeholder }) {
return (
<input
type={type}
value={value}
onChange={onChange}
placeholder={placeholder}
/>
);
}
3. Context API для глубокой иерархии
Когда компоненты находятся далеко друг от друга, используем Context:
// Создаём контекст
const ThemeContext = createContext();
// Provider (обычно на уровне App)
function App() {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<Header />
<Main />
<Footer />
</ThemeContext.Provider>
);
}
// Consumer в глубоко вложенном компоненте
function DeepChild() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div style={{ background: theme === 'light' ? '#fff' : '#333' }}>
<button onClick={toggleTheme}>
Текущая тема: {theme}
</button>
</div>
);
}
4. useReducer для сложного состояния
Для управления сложным состоянием с несколькими операциями:
const initialState = { count: 0, history: [] };
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1,
history: [...state.history, state.count + 1]
};
case 'DECREMENT':
return {
count: state.count - 1,
history: [...state.history, state.count - 1]
};
default:
return state;
}
}
function Parent() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
<Counter count={state.count} dispatch={dispatch} />
<History items={state.history} />
</>
);
}
function Counter({ count, dispatch }) {
return (
<div>
<p>Счет: {count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>
+
</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>
-
</button>
</div>
);
}
5. Ref для прямого доступа к элементам
Иногда нужен прямой доступ к DOM или методам компонента:
// Child с useImperativeHandle
const TextInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => inputRef.current.value = ''
}));
return <input ref={inputRef} />;
});
// Parent
function Parent() {
const inputRef = useRef();
return (
<>
<TextInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>
Фокус на input
</button>
<button onClick={() => inputRef.current.clear()}>
Очистить
</button>
</>
);
}
6. State Management библиотеки (Zustand, Redux)
Для очень сложного состояния, используем внешние хранилища:
// Zustand пример
const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 }))
}));
function Parent() {
const { count, increment, decrement } = useCounterStore();
return (
<>
<Child1 count={count} onIncrement={increment} />
<Child2 count={count} onDecrement={decrement} />
</>
);
}
Сравнение подходов
| Подход | Когда использовать | Плюсы | Минусы |
|---|---|---|---|
| Props + Callbacks | Простые случаи | Простота, предсказуемость | Prop drilling |
| Context API | Глубокая иерархия | Избегает prop drilling | Может вызвать лишние ре-ренденры |
| useReducer | Сложное состояние | Структурировано | Больше кода |
| Refs | Прямой доступ | Мощно | Нарушает React парадигму |
| Zustand/Redux | Очень сложное состояние | Масштабируемо | Излишне для простых случаев |
Лучшие практики
- Начни с props - это самый простой и надёжный способ
- Избегай глубокого prop drilling - используй Context или другие инструменты
- Контролируй направление данных - одностороннее направление проще отследить
- Используй функции обратного вызова - не мутируй объекты напрямую
Двусторонняя коммуникация - это суть компонентной архитектуры React, и правильный выбор паттерна критичен для maintainability кода.