【问题标题】:How do I call an event handler or method in a child component from a parent?如何从父组件调用子组件中的事件处理程序或方法?
【发布时间】:2019-08-04 05:08:09
【问题描述】:

我正在尝试在 Material-UI 文档中实现类似于浮动操作按钮 (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方法呢?

鉴于我的列表组件位于选项卡内,而 FAB 位于整个选项卡结构之外,我不确定如何实际实现将项目添加到列表单击事件处理程序。

据我所知,我可以:

  1. 找到一种方法来连接父/子以通过各个级别传递事件处理程序(例如How to pass an event handler to a child component in React
  2. 找到一种方法来更好地组合组件/层次结构以将职责置于正确的级别(例如,使用功能组件删除组件并将其与this 放在同一个文件中?)

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

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

【问题讨论】:

  • 这是一个相当广泛的问题。有很多可能的方法来组织功​​能,您的示例目前没有提供任何关于自定义组件的性质或 FAB 执行的操作类型的见解。在某些情况下,两者之间不需要任何直接的交互。这在很大程度上取决于您的整体状态管理方法。
  • 感谢@RyanCogswell,我已将我的问题更新为更具体。如果您对如何管理列表的状态有任何想法,同时在子组件外部添加按钮,我将不胜感激!

标签: reactjs event-handling components material-ui


【解决方案1】:

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

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

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

【讨论】:

  • 感谢@DoHn 的回答!我的示例只是子组件中的内容列表,FAB 只是添加按钮,因为这是主要操作。 Material-UI 文档只是将按钮显示在选项卡结构之外,因此不清楚他们会如何设想使用这种结构的人。
  • 好的,这更有意义,想用代码更新你的答案吗?如果是这样,我可以将其标记为答案。
  • 好吧,这很有意义,我来自一个双向绑定风格的前端框架,很难想像反应......一个。大大地。捆绑。更新后重构。谢谢!
  • 只是糟透了,newItem 当前是在子组件中创建的......必须以某种方式提升它。
  • 我必须说,肯定有一种方法可以更好地构建组件。如果您在 ListOfThingsComponent 中创建新项目并仅在其中使用该新项目,那么将 fab 放在它之外是不值得的。我确定您有理由将其保留在外面,但是根据我从您的代码中了解到的情况,这似乎不值得。
猜你喜欢
  • 2016-06-17
  • 2019-11-11
  • 2021-05-02
  • 1970-01-01
  • 2016-12-08
  • 2020-06-15
  • 1970-01-01
相关资源
最近更新 更多