Реакция — вызов функции из родственного компонента

#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