Как передать данные от одного компонента к другому?
В React и React Native передача данных между компонентами — ключевая часть архитектуры приложения. React использует односторонний поток данных (unidirectional data flow), что означает: данные передаются от родителя к потомку через props. Это делает управление состоянием предсказуемым и простым для отладки. Тем не менее, существует несколько способов передать данные между компонентами в разных ситуациях — от простых до более сложных.
1. Передача данных от родительского компонента к дочернему с помощью props
Это самый прямой и основной способ передачи данных.
Пример:
function Parent() {
const userName = 'Алиса';
return <Child name={userName} />;
}
function Child({ name }) {
return <Text>Привет, {name}</Text>;
}
-
Parent передаёт строку 'Алиса' в Child через props.
-
Child получает её как name.
Можно передавать любые типы данных: строки, числа, объекты, массивы, функции и даже JSX-компоненты.
2. Передача функции от родителя к дочернему компоненту
Если нужно сообщить родителю о действии в дочернем компоненте, родитель может передать функцию как проп.
Пример:
function Parent() {
const handleSelect = (value) => {
console.log('Выбрано:', value);
};
return <Child onSelect={handleSelect} />;
}
function Child({ onSelect }) {
return (
<Button title="Нажми" onPress={() => onSelect('React Native')} />
);
}
-
Parent передаёт функцию handleSelect в Child.
-
Child вызывает эту функцию с аргументом, который передаётся родителю.
Этот способ называется "подъём состояния" (state lifting), когда логика управления данными находится в родителе, а действия инициируются из потомков.
3. Передача данных от дочернего компонента к родительскому (обратная связь)
React не позволяет напрямую передавать данные от дочернего к родителю. Но это решается тем, что родитель передаёт функцию-обработчик, как было выше.
Пример с формой:
function Parent() {
const \[email, setEmail\] = useState('');
return <EmailInput onChange={setEmail} />;
}
function EmailInput({ onChange }) {
return (
<TextInput
placeholder="Введите email"
onChangeText={(text) => onChange(text)}
/>
);
}
-
Родитель передаёт setEmail в EmailInput.
-
EmailInput вызывает onChange, передавая текст обратно родителю.
4. Передача данных между компонентами, не связанными напрямую
Когда два компонента не находятся в отношениях родитель-потомок, передача данных становится более сложной. Возможные решения:
A) Контекст (React Context API)
Контекст позволяет создать глобальное хранилище данных, к которому могут обращаться любые компоненты внутри дерева компонентов, минуя промежуточные слои.
Создание контекста:
const UserContext = React.createContext();
Родитель предоставляет данные:
function App() {
const user = { name: 'Алиса', role: 'админ' };
return (
<UserContext.Provider value={user}>
<Profile />
</UserContext.Provider>
);
}
Потомок получает данные:
function Profile() {
const user = useContext(UserContext);
return <Text>Пользователь: {user.name}</Text>;
}
Контекст идеален для:
-
Текущего пользователя
-
Тем оформления
-
Языков (i18n)
-
Настроек приложения
B) Навигационные параметры (React Navigation)
Если вы переходите с одного экрана на другой, можно передать данные через параметры навигации.
Передача:
navigation.navigate('Profile', { userId: 123 });
Получение:
function ProfileScreen({ route }) {
const { userId } = route.params;
return <Text>ID пользователя: {userId}</Text>;
}
Или через хук:
import { useRoute } from '@react-navigation/native';
const route = useRoute();
const { userId } = route.params;
Навигационные параметры удобны для одноразовой передачи данных между экранами (в отличие от глобального состояния).
C) Глобальное состояние (Redux, Zustand, Jotai, Recoil и др.)
Для сложных приложений, где данные изменяются во многих местах, используется централизованное хранилище состояний, например:
Пример с Redux:
// store.js
const userSlice = createSlice({
name: 'user',
initialState: { name: '', role: '' },
reducers: {
setUser: (state, action) => {
state.name = action.payload.name;
state.role = action.payload.role;
}
}
});
Использование в любом компоненте:
const user = useSelector(state => state.user);
const dispatch = useDispatch();
dispatch(setUser({ name: 'Алиса', role: 'admin' }));
Глобальные хранилища позволяют синхронизировать данные между экранами, списками, формами и другими элементами UI, не связывая их напрямую через props.
5. Использование useImperativeHandle и forwardRef
Когда родитель хочет управлять дочерним компонентом напрямую, можно использовать ссылки (refs).
Пример:
const CustomInput = React.forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focusInput: () => inputRef.current.focus(),
}));
return <TextInput ref={inputRef} />;
});
function Parent() {
const ref = useRef();
return (
<>
<CustomInput ref={ref} />
<Button title="Фокус" onPress={() => ref.current.focusInput()} />
</>
);
}
Этот подход используется редко, в основном для управления поведением дочерних компонентов, таких как TextInput, Modal, ScrollView и т.д.
6. Использование событий (через EventEmitter)
В сложных сценариях (например, когда компонент находится вне дерева NavigationContainer) можно использовать шину событий:
import { EventEmitter } from 'events';
const emitter = new EventEmitter();
function Sender() {
return (
<Button
title="Отправить"
onPress={() => emitter.emit('myEvent', { message: 'Привет' })}
/>
);
}
function Receiver() {
useEffect(() => {
emitter.on('myEvent', (data) => {
console.log(data.message);
});
return () => emitter.removeAllListeners('myEvent');
}, \[\]);
return <Text>Готов к получению</Text>;
}
Однако EventEmitter не рекомендуется как основной способ передачи данных. Он может быть полезен в плагинах или библиотеках, но сложен для отладки и масштабирования.
7. Локальное хранилище и асинхронные источники
Если компоненты сильно изолированы и работают в разное время, можно сохранять данные во внешние хранилища:
-
AsyncStorage — для постоянного хранения
-
SecureStore — для защищённого хранения
-
SQLite или Realm — для локальной базы данных
-
fetch()/axios — для передачи данных через сервер
Пример:
import AsyncStorage from '@react-native-async-storage/async-storage';
async function saveUser(user) {
await AsyncStorage.setItem('user', JSON.stringify(user));
}
async function loadUser() {
const json = await AsyncStorage.getItem('user');
return JSON.parse(json);
}
В React Native существует гибкая система передачи данных, начиная от props и заканчивая глобальными сторами и контекстами. Выбор подхода зависит от конкретной задачи: связаны ли компоненты напрямую, нужно ли отслеживать изменения, как долго должны храниться данные и т.д.