Межкомпонентная связь в React
Недавно начал изучать ReactJS и столкнулся с проблемой.
Мое приложение является списком с фильтрами и кнопкой для изменения отображения списка. В настоящий момент использую три компонента: <List />, <Filters /> and <TopBar />
. Теперь когда я, соответственно, изменяю значение фильтранеобходимо вызвать метод в компоненте <List />
для обновления данных в нем.
Как я могу заставить эти три компонента взаимодействовать друг с другом? Или мне необходимо какая-то глобальная модель хранения данных, в которой я могу изменить их?
Есть несколько вариантов реализации связи между компонентами. Выбор одного из них будет зависеть от того, как вы планируете организовать сами компоненты. Несколько примеров, которые можно предложить:
<Filter />
является дочерним компонентом<List />
<Filter />
и<List />
являются дочерними компонентами в неком родительском<Filter />
и<List />
являются отдельными компонентами в корневой директории
Вариант 1
<Filter />
является дочерним компонентом <List />
. В этом случае можно передать обработчик из <List />
в <Filter />
, который может вызываться на событие onChange
, чтобы отфильтровать список по введенному значению.
Ссылка на пример Межкомпонентная связь в React #1
var Filters = React.createClass({
handleFilterChange: function(e) {
this.props.updateFilter(e.target.value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="фильтр" className="form-control" />;
}
});
var List = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
})
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
var content;
if (displayedItems.length > 0) {
var items = displayedItems.map(function(item) {
return <li>{item}</li>
});
content = <ul>{items}</ul>
} else {
content = <p>Нет совпадений</p>
}
return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<h4>Результат</h4>
{content}
</div>
)
}
})
ReactDOM.render(
<List />,
document.getElementById('container')
)
- в момент события
onChange
поля вводаinput
мы вызываем методhandleFilterChange
. Метод перехватывает событие наinput
и передает его значение в свойствоupdateFilter
компонента<List />
- изменение свойства
updateFilter
компонента<Filters />
вызывает методhandleFilterUpdate
родительского компонента<List />
с переданным в него значением изinput
.
Вся магия кроется в свойстве updateFilter
компонента <List />
. Пример простой, но здесь есть несколько интересных моментов.
Вариант 2
Рассмотрим вариант, когда <Filter />
и <List />
являются дочерними компонентами в неком родительском. Он будет аналогичен первому варианту, но в этом варианте родительский компонент будет передавать передавать уже отфильтрованный список в <List />
. Большой плюс этого варианта в том, что он позволяет выделить <List />
из <Filter />
в отдельные компоненты.
Ссылка на пример Межкомпонентная связь в React #2
var Filter = React.createClass({
handleFilterChange: function(e) {
this.props.updateFilter(e.target.value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="фильтр" className="form-control" />;
}
});
var List = React.createClass({
render: function() {
var content;
console.log(this.props.items.length);
if (this.props.items.length > 0) {
var items = this.props.items.map( function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>;
} else {
content = <p>Нет совпадений</p>
}
return (
<div className='results'>
<h4>Результат</h4>
{content}
</div>
);
}
});
var ListComponent = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
})
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
return (
<div>
<Filter updateFilter={this.handleFilterUpdate} />
<List items={displayedItems} />
</div>
)
}
})
ReactDOM.render(
<ListComponent />,
document.getElementById('container')
)
Здесь у нас появился родительский компонент <ListComponent />
, который связывает в себе компоненты <Filter />
и <List />
.
- в момент события
onChange
поля вводаinput
мы вызываем методhandleFilterChange
. Метод перехватывает событие наinput
и передает его значение в свойствоupdateFilter
. - изменение свойства
updateFilter
компонента<Filters />
вызывает методhandleFilterUpdate
родительского компонента<ListComponent />
с переданным в него значением изinput
. - изменение состояния
state
компонента<ListComponent />
вызывает методrender
для перерисовки компонента. А в методеrender
обновляется свойствоitems
дочернего компонента<List />
. - в свою очередь обновление свойства
items
компонента<List />
вызывет егоrender
с новыми параметрами.
Вариант 3
Когда компоненты не могут общаться между собой по принципу "родитель-потомок", то официальная документация рекомендует создать глобальную систему событий.