React Redux:更多的容器与更少的容器



随着我进一步将redux+react实现到一个相当复杂的应用程序中,该应用程序依赖于加载单个页面的许多API请求,我很难决定在页面的根位置设置一个容器组件是否更好,该组件处理所有异步内容并将道具传递给哑组件,v.s.具有多个容器组件,这些组件只关心它们所需的数据,以及获取它们所需要的数据。我在这两种模式之间来回切换,发现它们都有优点/缺点:

如果我在顶部放一个容器组件:

  • pro:所有isFetching道具和fetchSomeDependency()动作都可以在一个地方处理
  • con:真正令人恼火的缺点是,我发现自己不得不通过多个组件转发道具和回调,而树中间的某些组件最终与此绑定

这里有一个问题的可视化示例,显示了道具方面所需的关系:

<MyContainer
  data={this.props.data}
  isFetchingData={this.props.isFetchingData}
  fetchData={this.props.fetchData}
 >
   {!isFetchingData && 
     <MyModal
        someData={props.data}
        fetchData={props.fetchData}
      >
       <MyModalContent
         someData={props.data}
         fetchData={props.fetchData}
       >
          <SomethingThatDependsOnData someData={props.someData} />
          <SomeButtonThatFetchesData  onClick={props.fetchData} />
        </MyModalContent>
      </MyModal>
    }
 </MyContainer>

正如你所看到的,<MyModal /><MyModalContent />现在需要关注与之无关的道具,视为语气词应该能够被重复使用,并且只关注语气词的风格品质。

一开始,上面的内容似乎很好,但当我使用100多个组件时,一切都变得非常复杂,我发现这些顶级容器组件的复杂性太高,我不喜欢,因为它们中的大多数(在我正在使用的应用程序中(都依赖于3个以上API请求的响应。

然后我决定尝试多个容器:

  • pro:完全无需转发道具。在某些情况下这样做仍然有意义,但它要灵活得多
  • pro:重构更容易。我感到惊讶的是,我可以在不破坏任何东西的情况下显著地移动和重构组件,而在其他模式中,事情破坏了很多
  • pro:每个容器组件的复杂性要低得多。我的mapStateToPropsmapDispatchToProps更特定于它所在组件的用途
  • con:任何依赖异步内容的组件都需要自己处理isFetching状态。这增加了在单个容器组件中处理的模式中不需要的复杂性

因此,主要的困境是,如果我使用一个容器,我会在根容器和叶组件之间的组件中获得这种不必要的复杂性。如果我使用多个容器,叶组件会变得更加复杂,并且最终会出现需要担心isFetching的按钮,即使按钮不应该担心这一点。

我想知道是否有人找到了避免这两种缺点的方法,如果是,你遵循什么"经验法则"来避免这种情况?

谢谢!

我一直认为,容器位于逻辑组件组的最顶端,而不是根/应用程序组件。

因此,如果我们有一个简单的搜索应用程序来显示结果,并假设组件高度是这样的

<Root> <- setup the app
  <App>
    <Search/> <- search input
    <Results/> <- results table
  </App>
</Root>

我会使SearchResults的redux感知容器。因为react组件被认为是可组合的。您可能有其他组件或页面需要显示ResultsSearch。如果您将数据获取和存储意识委托给根或应用程序组件,则会使组件变得相互依赖/应用程序。当你必须实施更改时,这会使事情变得更加困难,现在你必须更改所有使用它们的地方。

如果组件之间确实有紧密耦合的逻辑,则可能会出现这种情况。即便如此,我还是要说,你应该创建一个容器来包装你紧密耦合的组件,因为如果没有彼此,它们就无法实际使用。

Redux的作者Dan Abramov建议您在需要容器组件时使用它们。也就是说,一旦有太多的道具在组件之间来回布线,就应该使用容器了。

他称之为"正在进行的重构过程"。

请参阅本文:https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0

我甚至不会考虑使用单个容器的方法。它几乎完全否定了redux的所有优点。如果您的所有状态都在一个位置,并且所有回调都位于一个位置(根组件(,那么就不需要有状态管理系统。

不过,我认为还有一线路要走。我正在制作一个应用程序,我已经使用了大约5周(兼职(,现在已经有3000行了。它有3个层次的视图嵌套,一个我自己实现的路由机制,以及需要修改状态的10多个层次嵌套的组件。我基本上为每个大屏幕都有一个redux容器,它的工作效果非常好。

然而,如果我点击我的"客户端"视图,我会得到一个客户端列表,这很好,因为我的客户端视图位于redux容器中,并获得作为道具传递的客户端列表。然而,当我点击一个客户端时,我真的很犹豫是否为单个客户端的配置文件做另一个redux容器,因为这只是传递道具的一个附加级别。看起来,根据应用程序的范围,您可能希望通过redux容器传递最多1-2个级别的道具,如果超过这个级别,那么只需创建另一个redux容器。再说一遍,如果它是一个更复杂的应用程序,那么有时使用redux容器,有时不使用它们的混合可能会使可维护性更差。简言之,我的观点是尽可能减少redux容器,但绝对不能以牺牲复杂的道具链为代价,因为这是使用redux的要点。

所以我发布这个问题已经两年多了我一直在与React/Redux合作。我现在的一般经验如下所示:使用更多容器,但尽量以不需要了解isFetching的方式编写组件

例如,这里有一个我以前如何建立待办事项列表的典型例子:

  function Todos({ isFetching, items }) {
    if (isFetching) return <div>Loading...</div>
    return (
      <ul>
        {items.map(item => 
          <li key={item.id}>...</li>
        )}
      </ul>
    )
  }

现在我会做一些更像:

  function Todos({ items }) {
    if (!items.length) return <div>No items!</div>
    return (
      <ul>
        {items.map(item => 
          <li key={item.id}>...</li>
        )}
      </ul>
    )
  }

通过这种方式,您只需要连接数据,并且组件不关心异步API调用的状态。

大多数东西都可以这样写。我很少需要isFetching,但当我这样做时,通常是因为:

  1. 例如,我需要防止第二次单击提交按钮,这会进行API调用,在这种情况下,可能应该调用道具disableSubmit,而不是isFetching

  2. 当有东西在等待异步响应时,我想显式地显示一个加载程序。

现在,你可能会想,"在上面的todos示例中,当项目被提取时,你不想显示一个加载器吗?"但在实践中,实际上我不会。

原因是在上面的例子中,假设您正在轮询新的todo,或者当您添加todo时,您"重新蚀刻"了todo。在第一个例子中,每次发生这种情况时,todo都会消失,并经常被替换为"Loading…"。

然而,在与isFetching无关的第二个示例中,新项目被简单地添加/移除。在我看来,这是更好的用户体验。

事实上,在发布这篇文章之前,我查看了我编写的交换接口的所有UI代码,该接口非常复杂,没有发现任何一个必须将isFetching连接到我编写的容器组件的实例。

您不必在同一位置调度和加载您的状态。

换句话说,您的按钮可以调度异步请求,而另一个组件可以检查您是否正在加载。

例如:

// < SomeButtonThatFetchesData.js>
const mapDispatchToProps = (dispatch) => ({
  onLoad: (payload) => 
    dispatch({ type: DATA_LOADED, payload })
});

您需要一些中间件来处理加载状态。当您传递异步负载时,它需要更新isFetching

例如:

const promiseMiddleware = store => next => action => {
  if (isPromise(action.payload)) {
    store.dispatch({ type: ASYNC_START, subtype: action.type });

然后你可以在任何地方使用它:

// <MyContainer.js>
const mapStateToProps = (state) => ({
  isFetching: state.isFetching
});

并将数据加载到内部嵌套组件中:

// <SomethingThatDependsOnData.js>
const mapStateToProps = (state) => ({
  someData: state.someData
});

相关内容

  • 没有找到相关文章

最新更新