代码之家  ›  专栏  ›  技术社区  ›  shmeeps

用于react redux中代码重用的Chain connect/mapStateToProps/mapDispatchToProps函数

  •  1
  • shmeeps  · 技术社区  · 6 年前

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

    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组件旨在应用于页面上的任何组件,以便组件可以指示是否显示全局“加载”图标。因为它可以在任何地方使用,所以我创建了一个helper函数 MapDispatchToProps 在闭包中,并为每个组件生成一个ID,用于确保请求加载程序的所有组件指示它们不再需要它,并且可以隐藏全局加载程序。

    MapStateTops公司 使加载程序对组件可见,以及 允许他们请求加载程序显示或隐藏。

    加载.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会自动将对象合并在一起以使它们兼容吗?或者只需要最新的一组 MapStateTops公司 / mapDispatchTo 除非我手动合并?或者有没有更好的方法来获得这种我没有看到的可重用性?我真的宁愿避免为我们需要的每个组件创建一组定制的容器。

    2 回复  |  直到 4 年前
        1
  •  1
  •   markerikson    6 年前

    connect 将自动合并在一起的组合“道具传递到包装器组件”,“道具从这个组件的 mapState mapDispatch ". 该逻辑的默认实现很简单:

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

    所以,如果你把 在彼此周围,被包装的组件将接收 全部的 那些道具的名字只要不一样。如果这些道具中有任何一个有相同的名字,那么根据这个逻辑,它们中只有一个会出现。

        2
  •  1
  •   Matt Greenberg    6 年前

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

    我创建了这个例子没有行动的创造者或还原,让我知道如果你需要一个他们的例子。

    loadify.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));