【问题标题】:React - calling function from a sibling componentReact - 从兄弟组件调用函数
【发布时间】:2018-12-14 15:06:16
【问题描述】:

假设我有一个组件树如下

<App>
  </Header>
  <Content>
      <SelectableGroup>
          ...items
      </SelectableGroup>
  </Content>
  <Footer />
</App>

SelectableGroup 能够通过鼠标选择/取消选择它包含的项目。我将当前选择(选定项目的数组)存储在 redux 存储中,以便我的应用程序中的所有组件都可以读取它。

Content 组件已将ref 设置为SelectableGroup,这使我能够以编程方式清除选择(调用clearSelection())。像这样的:

class Content extends React.Component {

    constructor(props) {
        super(props);
        this.selectableGroupRef = React.createRef();
    }

    clearSelection() {
        this.selectableGroupRef.current.clearSelection();
    }

    render() {
        return (
            <SelectableGroup ref={this.selectableGroupRef}>
                {items}
            </SelectableGroup>
        );
    }

    ...

}

function mapStateToProps(state) {
    ...
}

function mapDispatchToProps(dispatch) {
    ...
}

export default connect(mapStateToProps, mapDispatchToProps)(Content);

我可以很容易地想象将这个clearSelection() 传递给Contents 的孩子。但是如何,这是我的问题,我可以从兄弟组件Footer 调用clearSelection() 吗?

  • 我是否应该从Footer 分派一个动作并将某种“请求调用清除选择”状态设置到 Redux 存储?在Content 中的componentDidUpdate() 回调中对此做出反应,然后立即调度另一个操作以重置此“请求调用清除选择”状态?
  • 或者有什么首选的方式来调用兄弟的函数?

【问题讨论】:

  • Select 是否充当受控输入?换句话说,您是否将其设置为&lt;Select ... value={valueFromProps}
  • 在 React 世界中,最好更改 props,而 ref 仅在没有其他选择时使用(例如通过调用 .focus() 强制输入具有焦点)
  • @skyboyer 我已将 Select 更改为 SelectGroup - 明确它不是 HTML Select 元素。它是一个 HOC,可以通过拖动鼠标来选择项目。使用其 ref 清除选择是其创建者的决定。
  • 但是你仍然可以用你自己的 HOC 包装它以拥有 valueonChange 属性,而不是在每个集成点依赖 ref

标签: reactjs react-redux


【解决方案1】:

你可以像这样使用 ref 来访问 Content 组件的全部功能

const { Component } = React;
const { render } = ReactDOM;

class App extends Component {
  render() {
    return (
      <div>
        <Content ref={instance => { this.content = instance; }} />
        <Footer clear={() => this.content.clearSelection() } />
    
      </div>
    );
  }
}

class Content extends Component {
  clearSelection = () => {
    alert('cleared!');
  }

  render() {
    return (
      <h1>Content</h1>
    );
  }
}

class Footer extends Component {

  render() {
    return (
      <div>Footer <button onClick={() => this.props.clear()}>Clear</button>
      </div>
    );
  }
}

render(
  <App />,
  document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

【讨论】:

  • 是的,这行得通。但是没有更清洁的东西吗?如果我想从更多组件中调用 clearSelection 怎么办?这样我就不得不将回调传递给他们所有人。
  • 是的,有很多方法可以做到这一点,但我认为这与我使用 React 所做的类似,但是当您使用 redux 时,您可以将函数保存在商店中,但这样做会破坏能力持久化和补充存储的内容以及干扰时间旅行调试,不推荐
【解决方案2】:

我认为上下文 API 在这种情况下会派上用场。我开始大量使用它来解决使用全局 state/redux 似乎不正确的情况,或者当您在组件树中通过多个级别向下传递道具时。

工作样本:

import React, { Component } from 'react'
export const Context = React.createContext()

//***************************//

class Main extends Component {

  callback(fn) {
    fn()
  }

  render() {
    return (
      <div>
        <Context.Provider value={{ callback: this.callback }}>
          <Content/>
          <Footer/>
        </Context.Provider>
      </div>
    );
  }
}

export default Main

//***************************//

class Content extends Component {

  render() {

    return (
      <Context.Consumer>
        {(value) => (
          <div onClick={() => value.callback(() => console.log('Triggered from content'))}>Content: Click Me</div>
        )}
      </Context.Consumer>
    )
  }
}

//***************************//

class Footer extends Component {

  render() {

    return (
      <Context.Consumer>
        {(value) => (
          <div onClick={() => value.callback(() => console.log('Triggered from footer'))}>Footer: Click Me</div>
        )}      
      </Context.Consumer>
    )
  }
}

//***************************//

假设内容和页脚并在其中自己的文件(content.js/footer.js)记得从 main.js 导入上下文

【讨论】:

    【解决方案3】:

    根据 Liam 的回答,在功能组件版本中:

    export default function App() {
      const a_comp = useRef(null);
    
      return (
        <div>
          <B_called_by_a ref={a_comp} />
          <A_callB
            callB={() => {
              a_comp.current.f();
            }}
          />
        </div>
      );
    }
    
    const B_called_by_a = forwardRef((props, ref) => {
      useImperativeHandle(ref, () => ({
        f() {
          alert("cleared!");
        }
      }));
      return <h1>B. my borther, a, call me</h1>;
    });
    
    
    
    function A_callB(props) {
      return (
        <div> A I call to my brother B in the button
          <button onClick={() => { console.log(props); props.callB();}}>Clear </button>
        </div>
      );
    }
    

    你可以签到codesandbox

    【讨论】:

      猜你喜欢
      • 2017-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多