← Назад к вопросам

Как организован двусторонний обмен информацией между родительским и дочерним компонентами?

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Очень сложное состояниеМасштабируемоИзлишне для простых случаев

Лучшие практики

  1. Начни с props - это самый простой и надёжный способ
  2. Избегай глубокого prop drilling - используй Context или другие инструменты
  3. Контролируй направление данных - одностороннее направление проще отследить
  4. Используй функции обратного вызова - не мутируй объекты напрямую

Двусторонняя коммуникация - это суть компонентной архитектуры React, и правильный выбор паттерна критичен для maintainability кода.

Как организован двусторонний обмен информацией между родительским и дочерним компонентами? | PrepBro