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

Как передать данные из дочернего компонента в родительский?

1.0 Junior🔥 151 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Механизмы передачи данных вверх по дереву

В React данные текут вниз через props, но для передачи данных наверх используются callback-функции. Это основной паттерн, хотя есть несколько вариантов в зависимости от сложности случая: простые callback-функции, React Context для большого дерева компонентов и состояние управления на уровне приложения.

Вариант 1: Callback-функции (самый простой и распространённый)

// Родительский компонент
import { useState } from 'react';
import { ChildForm } from './ChildForm';

export function ParentComponent() {
  const [childData, setChildData] = useState('');

  const handleDataFromChild = (data: string) => {
    console.log('Получены данные из дочернего компонента:', data);
    setChildData(data);
  };

  return (
    <div>
      <h1>Родитель</h1>
      <p>Данные от ребёнка: {childData}</p>
      <ChildForm onDataSend={handleDataFromChild} />
    </div>
  );
}

// Дочерний компонент
interface ChildFormProps {
  onDataSend: (data: string) => void;
}

export function ChildForm({ onDataSend }: ChildFormProps) {
  const [inputValue, setInputValue] = useState('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    onDataSend(inputValue);
    setInputValue('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="Введите что-нибудь"
      />
      <button type="submit">Отправить родителю</button>
    </form>
  );
}

Как это работает:

  1. Родитель создаёт функцию handleDataFromChild
  2. Передаёт эту функцию как prop onDataSend дочернему компоненту
  3. Ребёнок вызывает функцию с данными через onDataSend(inputValue)
  4. Данные попадают в состояние родителя через setChildData

Вариант 2: React Context (для сложных структур)

import { createContext, useContext, useState } from 'react';

// Создаём контекст
interface DataContextType {
  sharedData: string;
  updateSharedData: (data: string) => void;
}

const DataContext = createContext<DataContextType | undefined>(undefined);

export function DataProvider({ children }: { children: React.ReactNode }) {
  const [sharedData, setSharedData] = useState('');

  const updateSharedData = (data: string) => {
    setSharedData(data);
  };

  return (
    <DataContext.Provider value={{ sharedData, updateSharedData }}>
      {children}
    </DataContext.Provider>
  );
}

// Хук для использования контекста
export function useDataContext() {
  const context = useContext(DataContext);
  if (!context) {
    throw new Error('useDataContext должен быть использован внутри DataProvider');
  }
  return context;
}

// Дочерний компонент может обновить данные где угодно в дереве
export function ChildComponent() {
  const { updateSharedData } = useDataContext();

  const handleClick = () => {
    updateSharedData('Новые данные от ребёнка');
  };

  return <button onClick={handleClick}>Обновить контекст</button>;
}

// Другой компонент может прочитать обновленные данные
export function SiblingComponent() {
  const { sharedData } = useDataContext();
  return <p>Получено из контекста: {sharedData}</p>;
}

// Обёртка в App
export function App() {
  return (
    <DataProvider>
      <ChildComponent />
      <SiblingComponent />
    </DataProvider>
  );
}

Вариант 3: useRef + useImperativeHandle (для критичных случаев)

import { forwardRef, useImperativeHandle, useRef } from 'react';

interface ChildHandle {
  getData: () => string;
}

const ChildWithRef = forwardRef<ChildHandle, {}>(function Child(props, ref) {
  const dataRef = useRef('текущие данные');

  useImperativeHandle(ref, () => ({
    getData: () => dataRef.current,
  }));

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    dataRef.current = e.target.value;
  };

  return <input onChange={handleChange} />;
});

export function ParentWithRef() {
  const childRef = useRef<ChildHandle>(null);

  const handleGetData = () => {
    if (childRef.current) {
      console.log('Данные от ребёнка:', childRef.current.getData());
    }
  };

  return (
    <div>
      <ChildWithRef ref={childRef} />
      <button onClick={handleGetData}>Получить данные</button>
    </div>
  );
}

Сравнение подходов

Callback-функции: Используй для простых случаев, когда нужно отправить данные из ребёнка в ближайшего родителя. Простой, предсказуемый, легко понять поток данных.

Context: Используй когда данные нужны нескольким компонентам на разных уровнях вложенности. Избегает prop drilling (передача props через множество промежуточных компонентов).

useRef + useImperativeHandle: Редкий случай, когда нужен прямой доступ к методам ребёнка. Нарушает принцип декларативности React, используй только при необходимости.

Best Practices

Типизация callbacks: Всегда определяй тип функции в интерфейсе props. Это поможет избежать ошибок.

Именование: Callbacks обычно начинаются с on (onDataSend, onChange, onClick). Это стандартное соглашение в React.

Избегай излишних ре-рендеров: Используй useCallback для стабильности функций при передаче через props.

Как передать данные из дочернего компонента в родительский? | PrepBro