首页 > 解决方案 > 如何从父组件调用子组件中的事件处理程序或方法?

问题描述

我正在尝试在 Material-UI 文档中实现类似于 Floating Action Button (FAB) 的东西:

https://material-ui.com/demos/buttons/#floating-action-buttons

他们有类似的东西:

<SwipeableViews>
  <TabContainer dir={theme.direction}>Item One</TabContainer>
  <TabContainer dir={theme.direction}>Item Two</TabContainer>
  <TabContainer dir={theme.direction}>Item Three</TabContainer>
</SwipeableViews>
{
  fabs.map((fab, index) => (
    <Zoom>
      <Fab>{fab.icon}</Fab>
    </Zoom>
  ));
}

我有类似的东西:

<SwipeableViews>
  <TabContainer dir={theme.direction}>
    <ListOfThingsComponent />
  </TabContainer>
  <TabContainer dir={theme.direction}>Item Two</TabContainer>
  <TabContainer dir={theme.direction}>Item Three</TabContainer>
</SwipeableViews>
{
  fabs.map((fab, index) => (
    <Zoom>
      <Fab onClick={ListOfThingsComponent.Add???}>
        Add Item to List Component
      </Fab>
    </Zoom>
  ));
}

ListOfThingsComponent最初有一个Add按钮,效果很好。但我想像他们在文档中那样遵循 FAB 方法。为了做到这一点,添加按钮将驻留在子组件之外。那么如何从父组件中获取一个按钮来调用子组件的 Add 方法呢?

我不确定如何实际实现 Add Item to List click 事件处理程序,因为我的列表组件位于选项卡内,而 FAB 位于整个选项卡结构之外。

据我所知,我可以:

  1. 找到一种方法来连接父/子以通过各个级别传递事件处理程序(例如,如何将事件处理程序传递给 React 中的子组件
  2. 找到一种方法来更好地组合组件/层次结构以将责任放在正确的级别(例如,删除组件并将其放在同一个文件中,并this在范围内使用功能组件?)

我见过人们使用ref,但这只是感觉很hacky。我想知道它应该如何在 React 中完成。如果该示例更进一步并显示 FAB 的事件处理应该驻留在哪里,那就太好了。

提前谢谢,一如既往,我会发布我最终做的事情

标签: reactjsevent-handlingcomponentsmaterial-ui

解决方案


这取决于您期望点击执行的操作。他们只会改变给定项目的状态,还是会在该层次结构之外执行更改?晶圆厂是否会出现在每个选项卡中,或者您不确定?

我认为在大多数情况下,你最好做你以前做的事情。为每个 Tab 编写一个 CustomComponent 并让它自己处理 FAB。这可能是一种不好的方法的唯一情况是,如果您事先知道 FAB 的回调将在 CustomComponent 层次结构中进行更改,因为在这种情况下,从长远来看,您最终可能会遇到回调混乱(仍然,没有任何全局状态管理无法解决的问题)。

编辑后编辑:在 React 中使用按钮调用子组件内部的函数可以说是不可能的(不借助 Refs 或其他完全避免 React 的机制),因为它的数据流是单向的。该函数必须在某个共同的地方,在这种情况下,在安装按钮的组件和 ListOfThings 组件中。该按钮将调用该方法,该方法将更改“父”组件中的状态,并且新状态通过道具传递给 ListOfThings 组件:

export default class Parent extends Component {
  state = {
    list: []
  };

  clickHandler = () => {
    // Update state however you need
    this.setState({
      list: [...this.state.list, 'newItem']
    });
  }

  render() {
    return (
      <div>
        <SwipeableViews>
          <TabContainer dir={theme.direction}>
            <ListOfThingsComponent list={this.state.list /* Passing the state as prop */}/>
          </TabContainer>
          <TabContainer dir={theme.direction}>Item Two</TabContainer>
          <TabContainer dir={theme.direction}>Item Three</TabContainer>
        </SwipeableViews>
        {
          fabs.map((fab, index) => (
            <Zoom>
              <Fab onClick={this.clickHandler /* Passing the click callback */}> 
                Add Item to List Component
              </Fab>
            </Zoom>
          ))
        }
      </div>
    )
  }
}

如果你真的需要你的层次结构保持这样,你必须使用这种方法或某种形式的 ListOfThingsComponent 可以读取的全局状态管理。


推荐阅读