【问题标题】:Chain connect/mapStateToProps/mapDispatchToProps functions for code reuse in react-redux链 connect/mapStateToProps/mapDispatchToProps 函数用于 react-redux 中的代码重用
【发布时间】:2018-09-15 04:01:31
【问题描述】:

假设我有两个 redux 连接的组件。第一个是一个简单的 todo 加载/显示容器,将以下函数传递给connect()mapStateToProps 从 redux state 中读取 todos,mapDispatchToProps 用于请求 state 提供服务器最新的 todos 列表:

TodoWidgetContainer.js

import TodoWidgetDisplayComponent from '...'

function mapStateToProps(state) {
  return {
    todos: todoSelectors.getTodos(state)
  };
}

function mapDispatchToProps(dispatch) {
  return {
    refreshTodos: () => dispatch(todoActions.refreshTodos())
  };
}

connect(mapStateToProps, mapDispatchTo)(TodoWidgetDisplayComponent);

第二个 redux 组件旨在应用于页面上的任何组件,以便组件可以指示是否显示全局“加载”图标。由于这可以在任何地方使用,我创建了一个辅助函数,它将MapDispatchToProps 包装在一个闭包中并为每个组件生成一个 ID,用于确保所有请求加载器的组件都表明它们不再需要它,并且可以隐藏全局加载器。

功能基本如下,mapStateToProps 向组件公开加载器可见性,mapDispatchToProps 允许它们请求加载器显示或隐藏。

加载.js

function mapStateToProps(state) {
  return {
    openLoader: loaderSelectors.getLoaderState(state)
  };
}

function mapDispatchToProps() {
  const uniqId = v4();
  return function(dispatch) {
    return {
      showLoader: () => {
        dispatch(loaderActions.showLoader(uniqId));
      },
      hideLoader: () => {
        dispatch(loaderActions.hideLoader(uniqId));
      }
    };
  };
}

export default function Loadify(component) {
  return connect(mapStateToProps, mapDispatchToProps())(component);
}

所以现在,如果我有一个想要访问加载器的组件,我可以这样做:

import Loadify from '...'

class DisplayComponent = new React.Component { ... }

export default Loadify(DisplayComponent);

并且它应该给它一个唯一的ID,允许它请求加载器显示/隐藏,并且只要有一个组件请求它显示,加载器图标就会显示。到目前为止,这一切似乎都运行良好。

我的问题是,如果我想将此应用于 todos 组件,以便该组件可以请求/接收其 todos,同时还允许请求加载程序在处理时显示,我可以做类似的事情吗:

TodoWidgetContainer.js

import Loadify from '...'
import TodoWidgetDisplayComponent from '...'

function mapStateToProps(state) {
  return {
    todos: todoSelectors.getTodos(state)
  };
}

function mapDispatchToProps(dispatch) {
  return {
    refreshTodos: () => dispatch(todoActions.refreshTodos())
  };
}

const TodoContainer = connect(mapStateToProps, mapDispatchTo)(TodoWidgetDisplayComponent);

export default Loadify(TodoContainer);

假设没有重复的键,redux 会自动将对象合并在一起以使它们兼容吗?还是只需要最近的一组mapStateToProps/mapDispatchTo,除非我进行某种手动合并?或者有没有更好的方法来获得这种我没有看到的可重用性?我真的宁愿避免为我们需要的每个组件创建一组自定义容器。

【问题讨论】:

    标签: javascript reactjs ecmascript-6 redux react-redux


    【解决方案1】:

    connect 将自动合并“传递给包装器组件的道具”、“来自该组件的mapState 的道具”和“来自该组件的mapDispatch 的道具”的组合。该逻辑的默认实现很简单:

    export function defaultMergeProps(stateProps, dispatchProps, ownProps) {
      return { ...ownProps, ...stateProps, ...dispatchProps }
    }
    

    因此,如果您将多个级别的 connect 相互堆叠,则包装的组件将收到 所有 的这些道具,只要它们不具有相同的名称。如果这些道具中的任何一个确实具有相同的名称,那么根据这个逻辑,它们中只会出现一个。

    【讨论】:

      【解决方案2】:

      好的,这就是我要做的。创建一个高阶组件(HOC),为你的减速器添加一个新的微调器引用。 HOC 将通过绑定到生命周期方法来初始化和销毁​​对 redux 中微调器的引用。 HOC 将为基础组件提供两个属性。第一个是isLoading,它是一个接受布尔参数的函数; true 开启,false 关闭。第二个属性是spinnerState,它是微调器当前状态的只读布尔值。

      我在没有动作创建者或缩减者的情况下创建了这个示例,如果您需要它们的示例,请告诉我。

      加载.jsx

      /*----------  Vendor Imports  ----------*/
      import React from 'react';
      import { connect } from 'react-redux';
      import v4 from 'uuid/v4';
      
      /*----------  Action Creators  ----------*/
      import {
        initNewSpinner,
        unloadSpinner,
        toggleSpinnerState,
      } from '@/wherever/your/actions/are'
      
      
      const loadify = (Component) => {
        class Loadify extends React.Component {
      
          constructor(props) {
            super(props);
            this.uniqueId = v4();
            props.initNewSpinner(this.uniqueId);;
            this.isLoading = this.isLoading.bind(this);
          }
      
          componentWillMount() {
            this.props.unloadSpinner(this.uniqueId);
          }
      
          // true is loading, false is not loading
          isLoading(isOnBoolean) {
            this.props.toggleSpinner(this.uniqueId, isOnBoolean);
          }
      
          render() {
            // spinners is an object with the uuid as it's key
            // the value to the key is weather or not the spinner is on.
            const { spinners } = this.props;
            const spinnerState = spinners[this.uniqueId];
            return (
              <Component isLoading={this.isLoading} spinnerState={spinnerState}  />
            );
          }
      
        }
      
        const mapStateTopProps = state => ({
          spinners: state.ui.spinners,
        });
      
        const mapDispatchToProps = dispatch => ({
          initNewSpinner: uuid => dispatch(initNewSpinner(uuid)),
          unloadSpinner: uuid => dispatch(unloadSpinner(uuid)),
          toggleSpinner: (uuid, isOn) => dispatch(toggleSpinnerState(uuid, isOn))
        })
      
        return connect(mapStateTopProps, mapDispatchToProps)(Loadify);
      
      };
      
      export default loadify;
      

      用例示例

      import loadify from '@/location/loadify';
      import Spinner from '@/location/SpinnerComponent';
      
      class Todo extends Component {
      
        componentWillMount() {
          this.props.isLoading(true);
          asyncCall.then(response => {
            // process response
            this.props.isLoading(false);
          })
        }
      
        render() {
          const { spinnerState } = this.props;
          return (
            <div>
              <h1>Spinner Testing Component</h1>
              { spinnerState && <Spinner /> }
            </div>
          );
        }
      
      }
      
      // Use whatever state you need
      const mapStateToProps = state => ({
        whatever: state.whatever.youneed,
      });
      
      // use whatever dispatch you need
      const mapDispatchToProps = dispatch => ({
        doAthing: () => dispatch(doAthing()),
      });
      
      // Export enhanced Todo Component
      export default loadify(connect(mapStateToProps, mapDispatchToProps)(Todo));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-27
        • 1970-01-01
        • 2018-06-15
        • 2019-10-03
        • 2020-06-15
        • 2017-08-18
        • 2020-06-01
        • 2018-05-10
        相关资源
        最近更新 更多