Как передавать данные между компонентами в ReactJS
2 года назад·3 мин. на чтение
В React можно разными способами передавать данные между компонентами. Применимость каждого способа определяется направлением движения данных. Данные могут двигаться от дочернего компонента к родительскому или наоборот. Данные могут двигаться глубоко - от корневого элемента до элемента-потомка. Данными могут обмениваться соседние элементы. В этой статье мы рассмотрим как передавать данные в каждом конкретном случае.
Есть несколько типичных ситуаций передачи данные между компонентами в React:
- от родительского компонента к дочернему;
- от дочернего компонента к родительскому;
- между соседними компонентами;
- от компонента к компоненту-потомку (через несколько уровней вниз);
- от компонента к компоненту-предку (через несколько уровней вверх).
- через пропсы;
- используя callback-функцию;
- пробросом пропсов от уровня к уровню (prop drilling);
- при помощи контекста (React Context AP);
- через хранилище (store);
От родительского компонента к дочернему
Наиболее простой и часто встречающийся случай - это случай, когда дочерний компонент принимает данные от родителя через пропсы.import { useState } from 'react' const Parent = () => { const [value, setValue] = useState('') const handleChange = (event) => { setValue(event.target.value) } return ( <div> <input type="text" onChange={handleChange } /> {/* передаем проп в дочерний компонент */} <Child value={value} /> </div> ) } const Child = ({ value }) => { return ( <span>Value is: {value || '<Not set>'}</span> ) }
От дочернего компонента к родительскому
Если необходимо передать данные от дочернего реакт компонента к родительскому, используются функции обратного вызова (callback-функции).import { useState } from 'react' const Child = ({ onChange }) => { const handleChange = (event) => { onChange(event.target.value) // callback-функция } return ( <input type="text" onChange={handleChange} /> ) } const Parent = () => { const [value, setValue] = useState('') const handleChange = (value) => { setValue(value) } return ( <div> <span>Value is: {value || '<Not set>'}</span> <Child onChange={handleChange} /> </div> ) }
Между соседними компонентами
Данные между соседними компонентами, т.е. между компонентами на одном уровне, можно передать через общий предок. Обычно данные от одного Реакт компонента передаются вверх, в компонент-предок, через callback-функцию, а компонент-предок передает их в другой компонент через проп.import { useState } from 'react' const Parent = () => { const [value, setValue] = useState('') const handleChange = (value) => { setValue(value) } return ( <div> <Sibling1 onChange={handleChange} /> <Sibling2 value={value} /> </div> ) } const Sibling1 = ({ onChange }) => { const handleChange = (event) => { onChange(event.target.value) } return ( <input type="text" onChange={handleChange} /> ) } const Sibling2 = ({ value}) => { return ( <span>Value is: {value || '<Not set>'}</span> ) }
Через несколько уровней вверх/вниз
Если компоненты находится в несколько уровнях друг от друга, то также можно передать проп. Этот проп придется описывать во всех компонентах на всех промежуточных уровнях. Эта ситуация называется prop drilling. Если уровней много, то такой способ покажется не очень удобным. Если нужно передавать данные на несколько уровней вверх, то также придется описывать и вызывать callback-функцию на всех промежуточных уровнях. Однако, в подобных случаях можно использовать Context API (пример которого приведен ниже), или state management библиотеки, такие как Redux, MobX, Recoil и т.д.Исходный код Подробное руководство по Reactimport { useState, useContext, createContext } from 'react' // создаем контекст const ValueContext = createContext() // Component1 записывает данные в контекст ValueContext const Component1 = () => { const { setValue } = useContext(ValueContext) const handleChange = (event) => { setValue(event.target.value) } return ( <input type="text" onChange={handleChange} /> ) } // Component2 читает данные из контекста ValueContext const Component2 = () => { const { value } = useContext(ValueContext) return ( <span>Value is: {value || '<Not set>'}</span> ) } // компоненты, которым необходим доступ к контексту, // должны быть обернуты в Provider export default function App() { const [value, setValue] = useState('') return ( <ValueContext.Provider value={{ value, setValue }}> <Component1 /> <Component2 /> </ValueContext.Provider> ) }
Проп key для пересоздания компонента в ReactJS
2 года назад·2 мин. на чтение
Знали ли вы, что проп key может быть полезен не только при рендеринге списка компонентов. Проп key можно использовать и для того чтобы сбросить состояние одного компонента.
Что такое проп key
в ReactJS?
Это специальный проп, который может быть добавлен к любому компоненту. Он помогает механизму reconciliation (согласование), упрощая сравнение компонентов. Типичный сценарий использования key
- добавление его в компоненты списка. Он нужен для того чтобы React понимал, какой компонент списка был добавлен, удален или изменен.
const notes = [ { id: 1, title: 'React hooks', }, { id: 2, title: 'JSX', }, { id: 3, title: 'Redux', }, ]; const NotesList = ({ notes, onClick }) => { return ( <div className="notes-list"> {notes.map((note) => ( <p className="notes-list__item" key={note.id} onClick={() => onClick(note)} > {note.title} </p> ))} </div> ); };
Проп key
работает и вне списков
Проп key
может быть добавлен к абсолютно любому компоненту для того, чтобы сбросить нежелательное состояние этого компонента.
Например, в списке заметок есть поле для ввода текста. Если просто добавить это поле и ввести в него текст, то при выборе новой заметки слева - текст будет сохраняться. И, предположим, при выборе заметки мы хотим очистить это поле.
function App() { const [activeNote, setActiveNote] = useState(); const handleClick = (note) => { setActiveNote(note); }; return ( <div className="notes-container"> <NotesList notes={notes} onClick={handleClick} /> <Note title={activeNote?.title} /> </div> ); }
Это можно сделать, например, добавив пропconst Note = ({ title }) => { const [text, setText] = useState(); const handleChange = (event) => { setText(event.target.value); }; return ( <div className="note"> <p>{title}</p> <textarea className="note-textarea" value={text} onChange={handleChange} /> </div> ); };
text
в компонент Note
. И далее очищать его при изменении состояния activeNote
. Но изменение компонентов может быть невозможным, если мы используем компоненты из third-party библиотеки.
Сброс состояние экземпляра компонента
Пропkey
помогает React идентифицировать компонент. Его также можно использовать, чтобы сообщить React, что идентификатор компонента изменился и это вызовет полное повторное создание этого компонента. Добавим key={activeNote?.id}
к компоненту <Note />
.
Теперь, при изменении// ... return ( <div className="notes-container"> <NotesList notes={notes} onClick={handleClick} /> <Note title={activeNote?.title} key={activeNote?.id} /> </div> ); }
key
React пересоздаст компонент <Note />
.
Влияние на производительность
Хотя это хороший прием, который уменьшает количество кода, важно иметь ввиду, что этот подход заставляет React пересоздавать весь экземпляр компонента. В примере выше большая часть компонента<Note />
будет перерисована в любом случае при изменении activeNote
. Поэтому в этом случае это достаточно хорошее решение.
В реальных приложениях нужно ограничивать добавление key
к одиночным компонентам вне списков, а также избегать добавления key
на компоненты верхнего уровня. Это может стать причиной проблем с производительностью, которые трудно обнаружить.