【问题标题】:Generic Components with React Redux使用 React Redux 的通用组件
【发布时间】:2017-03-06 08:04:31
【问题描述】:

我在 redux 中的全局状态和可重用组件的概念上有点挣扎。

假设我有一个组件,它是一个文件选择器,我想在我的应用程序状态内的多个位置使用它。创建 action/reducers 会导致很多臃肿,因为我必须处理带有动态后缀的状态和其他奇怪的事情,这些事情并没有真正让我觉得是一种聪明的处理方式。

对这些事情的普遍共识是什么?我只能看到两个解决方案:

  • 使文件选择器组件具有本地状态(this.setState/this.getState

  • 使文件选择器成为全局状态的一部分,但在它自己独特的 reducer 中,一旦组件的操作完成,我就可以从中读取?

有什么想法/最佳实践吗?谢谢。

更新:为了澄清我描述的文件选择器不是一个纯粹在客户端工作的简单组件,而是必须从服务器获取数据,提供分页以及过滤等。这就是为什么我还想重用大部分客户端/服务器交互。显示这个组件的视图当然是愚蠢的,只显示来自状态的值 - 但是我如何在应用程序周围的多个地方重用动作/reducer?

【问题讨论】:

    标签: reactjs redux react-redux


    【解决方案1】:

    让 reducer 处理组件状态的多个实例。只需为您的 FileBrowser 组件的每个实例在应用程序中出现时定义一些“唯一”ID,然后将您的当前状态包装在一个对象中,并以这个 uniqueIds 作为键,将您的旧复杂状态作为值。

    这是我多次使用过的技术。如果您的所有 FileBrowser 在编译时都是已知的,您甚至可以在运行您的应用程序之前设置初始状态。如果您需要支持“动态”实例,只需创建一个为给定 id 初始化状态的 Action。

    您没有提供任何代码,但这里是一个可重复使用的 Todo reducer 的人为示例:

    function todos(state={}, action){
      switch(action.type){
        case 'ADD_TODO':
          const id = action.todoListId
          return {
             ...state,
             [id]: {
                ...state[id],
                todos: [ ...state[id].todos, action.payload ]
             }
          }
          // ... 
      }
    }
    

    【讨论】:

    • 是的,这就是我们目前解决它的方法。只是寻找一种更好的方法,因为它有很多移动部件和我想避免的动态调度。(操作需要携带 [id] 以便将它们包装在部分应用的函数等中。)
    • 你也可以让它成为一个自包含的组件,有自己的逻辑 + 数据,并把它放在你的 Redux 存储之外,例如使用 recompose 通过 reducer 函数来处理它的内部状态。但我的经验是,最终会有其他人想要了解它! :)
    【解决方案2】:

    通常,经验法则是您使用redux store 来管理应用程序中的数据,也就是存储从服务器获取的项目和本地react state 用于ui 行为,例如在您的情况下上传文件。我会制作一个纯 react 组件来管理文件上传,然后使用 redux-form 来管理特定的表单。

    这是我在项目中使用的组件示例

    import React, {Component, PropTypes} from 'react';
    import Button from 'components/Button';
    
    class FileButton extends Component {
      static propTypes = {
        accept: PropTypes.string,
        children: PropTypes.any,
        onChange: PropTypes.func.isRequired
      };
    
      render() {
        const {accept, children, onChange} = this.props;
        return <Button {...this.props} onClick={() => this.file.click()}>
          <input
            ref={el => this.file = $(el)}
            type="file"
            accept={accept}
            style={{display: 'none'}}
            onChange={onChange}
          />
          {children}
        </Button>;
      }
    }
    
    export default FileButton;
    

    【讨论】:

      【解决方案3】:

      我们得出的结论是,可重用组件必须有两种:

      • 哑组件,即仅接收道具并仅通过道具回调触发“动作”的组件。这些组件具有最小的内部状态或根本没有。这些是最常见的可重用组件,您的文件选择器可能会落在这种情况下。样式化的文本输入或自定义列表也是很好的例子。

      • 提供自己的操作和减速器的连接组件。这些组件在应用程序中有自己的生命,并且与其他组件相当独立。一个典型的例子是当应用程序严重失败时显示在其他所有内容之上的“顶部错误消息框”。在这种情况下,应用程序会触发一个“错误操作”,并使用适当的消息作为有效负载,并在接下来的重新渲染中,消息框显示在其余部分之上。

      【讨论】:

      • 在我的例子中,文件选择器是一个非常复杂的组件,它需要从服务器获取数据、过滤这些数据并提供分页等。这就是为什么让它只是一个愚蠢的组件会导致很多action/reducer 端的重复(当然视图是转储的)但它封装的逻辑相当复杂。
      猜你喜欢
      • 2017-07-24
      • 2019-09-05
      • 2017-09-12
      • 1970-01-01
      • 2021-01-28
      • 2017-11-08
      • 1970-01-01
      • 2016-08-31
      • 2019-06-14
      相关资源
      最近更新 更多