【问题标题】:How should child components in React communicate between each other in a clean and maintainable fashion?React 中的子组件应该如何以干净和可维护的方式相互通信?
【发布时间】:2016-12-17 22:33:31
【问题描述】:

我对 React 还很陌生,我正在尝试了解一种让子组件相互通信的干净方式。

在一个简单的组件中,我知道我可以利用 props 将数据传递给子组件,并使用回调让子组件将数据传递回父组件。

在稍微复杂一点的情况下,当我在一个父组件中有多个子组件时,子组件之间的通信会变得有点混乱。我不确定我应该如何为同一级别的子组件相互通信。

就我而言,我决定,也许我可以使用states。所以我会在父组件中有一个state 值,并将它传递给孩子的道具。类似地,父组件中的回调处理程序(从子组件调用)将有助于相应地设置状态,以便状态值通过 React 的绑定从一个子组件传递到另一个子组件。

伪代码可能类似于:

//Inside Parent Component
constructor() {
   //initialise state for the child components
   this.setState({testList: []});
}

render() {
    return (
        <div>
            <ChildA onSomething={this.onSomethingHandler} testList={this.state.testList} />
            <ChildB onSomethingElse={this.onSomethingElseHandler} testList={this.state.testList} />
        </div>
    );
}

onSomethingHandler(evt):void {
    if(blah.blah.blah) this.setState({testList: this.state.testList.splice().push(evt.value)};
}

onSomethingElseHandler(evt):void {
    //Some other complex biz logic...
    if(blah.blah.blah) this.setState({testList: this.state.testList.splice().push(somethingOtherStuffDueToLogic)};
}

//Inside ChildA Component
export IChildAProps {
    onSomething: (evt)=>void
}

render() {
    //Do some logic from the value in testList property
    if(this.state.testList == blah blah)...

    return (
        <button onClick={this.props.onSomething({id:321, value:"wassup! I'm ChildA."})}>ChildA</button>
    )
}

//Inside ChildB Component
export IChildBProps {
    onSomethingElse: (evt)=>void
}

render() {
    //Do some logic from the value in testList property
    if(this.state.testList == blah blah)...

    return (
        <button onClick={this.props.onSomething({id:123, value:"yo! I'm ChildB."})}>ChildB</button>
    )
}

此时,我开始怀疑这两个处理方法中的逻辑,即父组件中的onSomethingHandler()onSomethingElseHandler(),是否真的应该驻留在子组件本身中?我之所以想到这一点,是因为这些逻辑看起来像是子组件应该自己处理以达到其目的的东西。父组件不应该为他们这样做,否则它可能会变得混乱。但我别无选择,因为我如何处理他们的沟通。除此之外,我还创建了一个新状态,只是为了让他们交流。

到目前为止,这仍然是相对可控的。但是在我自己的实验中,它已经到了一个阶段,我将子组件嵌套在另一个子组件中,这些子组件需要在相同(或有时不同)级别的其他子组件之间进行通信。使用状态进行通信也意味着我到处都有许多个状态,这对我来说不是一个好主意。父组件最终使用了大量杂乱无章的回调处理程序方法来管理所有数据在组件树上上下的传播。

情况如此混乱,我至多可以这样说明:

您可以在上图中看到,ChildB 最终拥有另一个状态,只是为了帮助在其子组件之间传递该信息。

我确定我错过了一些我应该了解的关于 React 的知识。我在父组件中的回调似乎有点太多,只是为了处理数据传播。我应该如何真正以干净和可维护的方式组织子组件的通信?

【问题讨论】:

  • 如果我理解正确,那么您将共享资源(testList)从父级传递给所有子级。我建议不要这样做,因为它很难管理和控制/同步,如果由于某种原因这个testList 的实例在父级中发生变化会发生什么?简单地保持您在简单案例中描述的方式有什么问题?当 childA 发生某些事情时,它使用回调通知父级,然后父级决定 childB 是否需要知道它,如果需要,则通过将新道具传递给 childB 来传递该信息。
  • 但是如果ChildB_1 里面的ChildB 也需要那条信息呢?我是否会进一步传播该信息?在这种情况下,testList 看起来像是一个共享资源。但我认为我还有其他状态,例如this.state.panelOpen='true/false'this.state.currentMode=0/1/2 等。不知何故,我不断遇到需要将这些信息传递给不同级别的多个子组件的情况,这让我发疯了带着所有凌乱的回调。
  • 这很容易,除非我错过了什么。当您将信息从父级传递给 childB 时,您在 Parent.render 方法中执行此操作,然后它会看到 childB 的道具是“脏的”,因此它使用新道具调用 childB 的渲染。当childB.render 方法被调用时,它将使用新的道具调用childB_1.render 方法,该道具将具有此新信息。这似乎是一个很长的路要走,可以通过共享资源更轻松地完成某些事情,但是如果您考虑一下,这实际上可以简化事情,因为您不必担心其他组件的状态
  • @NitzanTomer 是的,这是有道理的!事实上,我最近也发现了这一点,React 会检查脏道具以重新渲染。唯一的缺点是我必须将道具绑定到一个状态。所以我最终还是为此目的为状态创建了一个数字,但是我通过这个大大减少了子组件中的回调和不必要的状态。但是,我仍然必须通过树将道具从一个组件向下传递到另一个组件,以传递信息。通常,中间组件甚至不需要 props,它们只是将其传递给其他需要它的子组件。
  • 为什么需要绑定 props 到 state?一个 react 组件应该有一个 render 方法,它使用它的 state 和 props 来决定它应该如何渲染。组件可以更改自己的状态(使用setState),这将导致组件再次被渲染。当父母希望再次渲染孩子时,它会更改该孩子的道具。您的父母可以使用其状态为孩子创建所需的道具,但状态不应绑定到道具。

标签: javascript reactjs typescript architecture


【解决方案1】:

最流行的方式是使用 Redux.js(或 Flux.js)来保持您的子组件状态。

http://redux.js.org/

如果你不喜欢调用第三方 js。你可以使用refs属性:

https://facebook.github.io/react/docs/more-about-refs.html

【讨论】:

    【解决方案2】:

    每个 React 程序员都会在某个时间点碰壁。我也是。答案是使用 Redux 进行状态管理。你已经体验过使用 React 的原生状态是多么乏味。

    Redux 是一种状态管理机制,可以与 React 结合使用。所以你不会使用 React 的 state,而是使用 Redux。

    Redux 提供了一个单独的 store,用于存储整个应用程序的状态。您可以使用 connect() 方法访问组件中的状态。

    但有一个警告。并非所有的 react 组件都连接到 Redux 存储。有两种类型的组件-

    • 智能/连接组件:连接到 redux 存储
    • 哑组件:依赖于连接的组件

    这个想法是通过 React 的 props 将状态从 redux 存储传递到 Connected 组件。连接的组件可以直接从存储中消费状态。哑组件不直接连接到 redux 存储。连接的组件还通过props 将状态传递给哑​​组件。所以你看,React 的状态完全被绕过了。现在,如果您想更改状态,则必须发生以下事件-

    1. 从 smart/dumb 组件触发一个事件。
    2. Actions 被分派到store
    3. Reducers根据actions创建新状态。
    4. 新状态将存储在store
    5. Store 将通过connect()props 将新状态返回给连接的组件
    6. 哑组件将通过props从连接组件接收新状态

    什么是 action 和 reducer?

    动作只不过是描述如何更改状态的 javascript 对象。

    Reducer 是一个“纯”函数,它根据分派到 store 的操作构建并返回新的状态树。

    Redux - http://redux.js.org/

    Redux-thunk - https://github.com/gaearon/redux-thunk

    Redux-saga - https://github.com/yelouafi/redux-saga

    【讨论】:

    • 感谢您的回答!我实际上知道redux,但我希望有一种香草方法来解决这个问题,这样我就不必使用额外的库。我认为这会更好,特别是如果我打算创建可以被其他人重用的组件,这些人可能会将 redux 作为其依赖项之一。
    • 感谢您简洁的解释。
    • @Mihir:嘿,但是您如何在子组件之间进行通信..??
    【解决方案3】:

    我们可以使用react context API

    上下文提供了一种通过组件树传递数据的方法,而无需在每个级别手动向下传递道具

    另外,请注意 Mark Erikson 在他的blog 中提到,

    是的,新的上下文 API 非常适合将数据传递给深度嵌套的组件 - 这正是它的设计目的。

    如果你只是使用 Redux 来避免传递 props,context 可以代替 Redux - 但你可能一开始就不需要 Redux。

    上下文也没有像 Redux DevTools 那样为您提供任何东西、跟踪您的状态更新的能力、添加集中式应用程序逻辑的中间件以及 Redux 支持的其他强大功能。

    要处理您提到的场景,上下文 API 是一个不错的选择,您不必为此使用其他库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-04
      • 1970-01-01
      • 2011-07-12
      • 2021-11-25
      • 1970-01-01
      • 2014-11-30
      • 1970-01-01
      相关资源
      最近更新 更多