#javascript #reactjs
Вопрос:
Допустим, у меня есть дерево компонентов следующим образом
lt;Appgt; lt;/Headergt; lt;Contentgt; lt;SelectableGroupgt; ...items lt;/SelectableGroupgt; lt;/Contentgt; lt;Footer /gt; lt;/Appgt;
Где SelectableGroup
можно выбрать/отменить выбор элементов, которые он содержит, с помощью мыши. Я сохраняю текущий выбор (массив выбранных элементов) в хранилище redux, чтобы все компоненты моего приложения могли его прочитать.
Content
Компонент установил значение a ref
SelectableGroup
, которое позволяет мне программно очистить выделение (вызов clearSelection()
). Что-то вроде этого:
class Content extends React.Component { constructor(props) { super(props); this.selectableGroupRef = React.createRef(); } clearSelection() { this.selectableGroupRef.current.clearSelection(); } render() { return ( lt;SelectableGroup ref={this.selectableGroupRef}gt; {items} lt;/SelectableGroupgt; ); } ... } function mapStateToProps(state) { ... } function mapDispatchToProps(dispatch) { ... } export default connect(mapStateToProps, mapDispatchToProps)(Content);
Я легко могу представить, как передать это clearSelection()
Content
своим детям. Но как, и это мой вопрос, я могу позвонить clearSelection()
из компонента «брат Footer
«?
- Должен ли я отправить действие из
Footer
и установить какое-то состояние «запрос на вызов четкого выбора» в хранилище Redux? Отреагируйте на это вcomponentDidUpdate()
обратномContent
вызове, а затем немедленно отправьте другое действие, чтобы сбросить это состояние «запрос на вызов с четким выбором»? - Или есть какой-либо предпочтительный способ вызова функций братьев и сестер?
Комментарии:
1.
Select
действует ли как контролируемый ввод? другими словами, вы настраиваете его какlt;Select ... value={valueFromProps}
?2. в мире реакций предпочтительнее менять реквизит, пока
ref
используется только в том случае, если нет другого выбора(скажем, чтобы заставить ввод сосредоточиться с помощью вызова.focus()
).3. @skyboyer я изменил
Select
наSelectGroup
— чтобы было ясно, что это не элемент выбора HTML. Это специальная функция, которая позволяет выбирать элементы с помощью перетаскивания мыши. Использование его ссылки для четкого выбора-это решение, принятое его создателями.4. но вы все равно можете обернуть его своим собственным специальным
value
оборудованием иonChange
реквизитом, вместоref
того, чтобы полагаться на каждую точку интеграции
Ответ №1:
Вы можете использовать ссылку для доступа ко всем функциям компонента содержимого, например
const { Component } = React; const { render } = ReactDOM; class App extends Component { render() { return ( lt;divgt; lt;Content ref={instance =gt; { this.content = instance; }} /gt; lt;Footer clear={() =gt; this.content.clearSelection() } /gt; lt;/divgt; ); } } class Content extends Component { clearSelection = () =gt; { alert('cleared!'); } render() { return ( lt;h1gt;Contentlt;/h1gt; ); } } class Footer extends Component { render() { return ( lt;divgt;Footer lt;button onClick={() =gt; this.props.clear()}gt;Clearlt;/buttongt; lt;/divgt; ); } } render( lt;App /gt;, document.getElementById('app') );
lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"gt;lt;/scriptgt; lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"gt;lt;/scriptgt; lt;div id="app"gt;lt;/divgt;
Комментарии:
1. Да, это работает. Но разве нет чего-то более чистого? Что делать, если я хотел бы вызвать clearSelection из большего количества компонентов? Таким образом, я вынужден передать обратный вызов всем им.
2. Да, есть много способов сделать это, но я думаю, что это похоже на то, что я сделал с помощью React, но поскольку вы используете redux, вы можете сохранить функцию внутри магазина, но это может нарушить способность сохраняться и регидрировать содержимое магазина, а также помешать отладке во времени, это не рекомендуется
Ответ №2:
Я думаю, что контекстный API пригодился бы в этой ситуации. Я начал часто использовать его в тех случаях, когда использование глобального состояния/переопределения казалось неправильным или когда вы передаете реквизиты через несколько уровней в дереве компонентов.
Рабочий образец:
import React, { Component } from 'react' export const Context = React.createContext() //***************************// class Main extends Component { callback(fn) { fn() } render() { return ( lt;divgt; lt;Context.Provider value={{ callback: this.callback }}gt; lt;Content/gt; lt;Footer/gt; lt;/Context.Providergt; lt;/divgt; ); } } export default Main //***************************// class Content extends Component { render() { return ( lt;Context.Consumergt; {(value) =gt; ( lt;div onClick={() =gt; value.callback(() =gt; console.log('Triggered from content'))}gt;Content: Click Melt;/divgt; )} lt;/Context.Consumergt; ) } } //***************************// class Footer extends Component { render() { return ( lt;Context.Consumergt; {(value) =gt; ( lt;div onClick={() =gt; value.callback(() =gt; console.log('Triggered from footer'))}gt;Footer: Click Melt;/divgt; )} lt;/Context.Consumergt; ) } } //***************************//
Предполагая содержимое и нижний колонтитул, а также наличие собственных файлов (content.js/footer.js) не забудьте импортировать контекст из main.js