Как передать данные из дочернего компонента в родительский?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Механизмы передачи данных вверх по дереву
В 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>
);
}
Как это работает:
- Родитель создаёт функцию handleDataFromChild
- Передаёт эту функцию как prop onDataSend дочернему компоненту
- Ребёнок вызывает функцию с данными через onDataSend(inputValue)
- Данные попадают в состояние родителя через 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.