Короткий ответ: в React дочерний компонент не меняет родителя напрямую. Родитель хранит состояние и передает в дочерний компонент callback-функцию. Дочерний компонент вызывает эту функцию и передает данные наверх
Схема:
Parent хранит state
Parent передает onChange в Child
Child вызывает onChange(value)
Parent обновляет state
Минимальный пример
Parent.tsx:
import { useState } from "react";
import { NameInput } from "./NameInput";
export function Parent() {
const [name, setName] = useState("");
return (
<div>
<NameInput onNameChange={setName} />
<p>Имя: {name}</p>
</div>
);
}
type NameInputProps = {
onNameChange: (value: string) => void;
};
export function NameInput({ onNameChange }: NameInputProps) {
return (
<input
onChange={(event) => onNameChange(event.target.value)}
placeholder="Введите имя"
/>
);
}
Здесь Parent владеет состоянием name, а NameInput только сообщает новое значение
Почему не нужно менять parent напрямую
В React данные обычно идут сверху вниз: родитель передает props дочерним компонентам. Если child должен повлиять на parent, он не берет state родителя силой, а вызывает функцию, которую родитель сам передал
Так код остается предсказуемым:
state живет в одном месте
child не знает внутренности parent
обновление проходит через явный callback
Передача объекта
Можно передавать не строку, а объект:
type UserFormData = {
name: string;
email: string;
};
type UserFormProps = {
onSubmit: (data: UserFormData) => void;
};
export function UserForm({ onSubmit }: UserFormProps) {
function handleSubmit() {
onSubmit({
name: "Dinar",
email: "dinar@example.com",
});
}
return <button onClick={handleSubmit}>Сохранить</button>;
}
В родителе:
export function Parent() {
function handleUserSubmit(data: UserFormData) {
console.log(data.email);
}
return <UserForm onSubmit={handleUserSubmit} />;
}
Если структура объекта изменится, TypeScript подсветит места, где callback вызывается неправильно
Типизация события input
Если обработчик вынесен отдельно:
import type { ChangeEvent } from "react";
function handleChange(event: ChangeEvent<HTMLInputElement>) {
onNameChange(event.target.value);
}
Для textarea будет HTMLTextAreaElement, для select — HTMLSelectElement. Не ставьте any, иначе можно случайно обратиться к несуществующему полю
Когда поднимать state выше
Если данные нужны только внутри дочернего компонента, не поднимайте их. Но если значение нужно родителю, соседнему компоненту или отправке формы, state лучше хранить на ближайшем общем уровне
Пример:
Parent
SearchInput
ResultsList
SearchInput меняет поисковую строку, а ResultsList показывает результат. Значит, строка должна жить в Parent
Частые ошибки
Первая ошибка — передавать результат вызова вместо функции:
<Child onChange={setName("Dinar")} />
Правильно:
<Child onChange={setName} />
Вторая ошибка — мутировать объект из props
Третья ошибка — использовать any для callback
Четвертая ошибка — хранить один и тот же state и в parent, и в child. Потом значения начинают расходиться
Пятая ошибка — называть callback слишком общо: onClick, хотя он передает email или всю форму. Лучше onEmailChange, onUserSubmit, onFilterChange
Самопроверка
Сделайте компонент CounterButton, который принимает onCountChange: (count: number) => void. Внутри child увеличивайте локальный счетчик и отправляйте новое значение родителю. Если TypeScript запрещает передать строку вместо числа, типизация работает
Что почитать дальше по TypeScript
Если нужен общий маршрут по теме, откройте рубрику TypeScript. Для соседних задач пригодятся эти разборы:
- App.tsx: что это за файл и как добавить TypeScript в React
- Create React App и TypeScript: как установить, запустить и проверить
- React TypeScript: как решить проблему с кастомным компонентом
- React и TypeScript: как настроить и использовать в проекте



