Из-за одностороннего потока данных в React часто бывает сложно увидеть, как данные могут передаваться от одного компонента к другому. Иногда требуется, чтобы данные могли передаваться от детей к родителям, от родителей к детям или между братьями и сестрами.

Вот пример структуры компонентов React, из которых состоит мое приложение:

App
 |
 |__ InputBar
 |
 |__ ToDoList
       |
       |__ToDoItem

От родителя к ребенку - используйте опору

Это самое простое направление передачи данных в React. Если у меня есть доступ к данным в моем родительском компоненте, к которым мне нужен доступ к моему дочернему компоненту, я могу передать его в качестве опоры дочернему при создании экземпляра в родительском компоненте.

В моем примере, если мне нужно передать что-то из приложения в ToDoList:

class App extends React.Component {
    render() {
    [... somewhere in here I define a variable listName which I think will be useful as data in my ToDoList component...]
        
        return (
            <div>
                 <InputBar/>
                 <ToDoList listNameFromParent={listName}/>
            </div>
        );
    }
}

Теперь в компоненте ToDoList, если я использую this.props.listNameFromParent, у меня будет доступ к этим данным.

От ребенка к родителю - используйте обратный вызов и состояния

Это немного сложнее. Если у меня есть данные в моем ребенке, к которым мой родитель нуждается в доступе, я могу сделать следующее:

  1. Определите обратный вызов в моем родителе, который принимает нужные мне данные в качестве параметра.
  2. Передайте этот обратный вызов как опору дочернему элементу (см. Выше).
  3. Вызовите обратный вызов, используя this.props. [Обратный вызов] в дочернем элементе (конечно, вставьте свое собственное имя, где указано [обратный вызов]), и передайте данные в качестве аргумента.

Вот как это могло бы выглядеть, если бы у меня были данные в ToDoItem, к которым мне нужно получить доступ в ToDoList:

class ToDoList extends React.Component {
    myCallback = (dataFromChild) => {
        [...we will use the dataFromChild here...]
    },
    render() {
        return (
            <div>
                 <ToDoItem callbackFromParent={this.myCallback}/>
            </div>
        );
    }
}

Теперь из ToDoItem мы можем передать что-нибудь в callbackFromParent:

class ToDoItem extends React.Component{
    someFn = () => {
        [...somewhere in here I define a variable listInfo which    I think will be useful as data in my ToDoList component...]
        this.props.callbackFromParent(listInfo);
    },
    render() {
        [...]
    }
};

ToDoList теперь сможет использовать listInfo в своей функции myCallback!

Но что, если я хочу использовать listInfo в другой функции в ToDoList, а не только в myCallback? С этой реализацией у меня будет доступ только как параметр, переданный в этот конкретный метод.

Легко: установите этот параметр как состояние в ToDoList. Вы можете почти думать об этом как о создании переменной в рамках ToDoList, к которой могут получить доступ все методы в этом компоненте. В этом случае мой код, определяющий ToDoList, может выглядеть примерно так:

class ToDoList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            listDataFromChild: null
        };    
    },
    myCallback = (dataFromChild) => {
        this.setState({ listDataFromChild: dataFromChild });
    },
    otherFn = () => {
        [...within this other function now I still have access to this.state.listDataFromChild...]
    }
    render() {
        return (
            <div>
                 <ToDoItem callbackFromParent={this.myCallback}/>
                 [...now here I can pass this.state.listDataFromChild as a prop to any other child component...]  
     
            </div>
        );
    }
});

Обратите внимание на использование стрелки в определении myCallback. Это позволяет нам избежать использования .bind при вызове, поскольку он сохранит контекст того, где он был вызван.

Между братьями и сестрами - объедините вышеперечисленное

Неудивительно, что для передачи данных между братьями и сестрами вы должны использовать родителя в качестве посредника. Сначала передайте данные от дочернего элемента к родительскому в качестве аргумента обратного вызова от родителя. Установите этот входящий параметр как состояние родительского компонента, затем передайте его как опору другому дочернему элементу (см. Пример выше). Затем брат или сестра может использовать данные как опору.

Поначалу передача данных между компонентами React может быть немного сложной (без использования Redux), но как только вы отработаете эти три метода, вы сможете передавать данные между любыми компонентами, которые захотите.