Redux 动作重用

Redux action reuse

我是 react / redux 的初学者。

我已经在我的应用程序中完成了一个基本组件 <HeatMap />,它的操作/reducer/store 运行良好。

我将渲染另一个 <HeatMap /> 具有不同的设置(道具)。

我想要做的是将这 2 个组件分开,因为当我 dispatch 一个更新 action 在一个中,另一个同时执行它。

问题 1

我试过这个来分隔商店中的状态

import heatMap from './heat-map1'
import {combineReducers} from 'redux';

export let reducers = combineReducers({
    heatMap1: heatMap,
    heatMap2: heatMap
});

combineReducers 和 connect存储中不同对象中的 2 个热图

export default connect((state)=> {
    return {
        onState: state.heatMap1.onState,
        config: state.heatMap1.config
    }
})(CHMSHeatMap1)

export default connect((state)=> {
    return {
        onState: state.heatMap2.onState,
        config: state.heatMap2.config
    }
})(CHMSHeatMap2)

这是正确的吗?

问题 2

因为 2 个组件都在调度动作时做出反应

我正在考虑将共享操作分开,但我认为这不是个好主意。或者问题不在这里。

那么你能告诉我是什么原因导致这个问题以及如何解决它吗?

这是我的减速器

import * as actionTypes from '../actions/heat-map';
import Immutable from 'immutable';


const onState = {
    fetching: 'FETCHING',
    error: 'ERROR',
    drawn: 'DRAWN'
};

const initialState = {
    onState: onState.fetching,
    config: {}
};


export default function heatMapReducer(state = initialState, action) {
    let immutableState = Immutable.fromJS(state);
    switch (action.type) {
        case actionTypes.INITIALIZING:
            return immutableState.set('onState', onState.drawn).set('config', action.payload.initConfig).toJS();
        case actionTypes.FETCH_DATA_REQUEST:
            return immutableState.set('onState', onState.fetching).toJS();
        case actionTypes.FETCH_DATA_SUCCESS:
            return immutableState.set('onState', onState.drawn).setIn(['config','series',0,'data'],Immutable.fromJS(action.payload.mapData.data)).toJS();
        case actionTypes.FETCH_DATA_FAILURE:
            return immutableState.set('onState', onState.error).set('config', action.payload.mapData).toJS();
        default:
            return state;
    }
}

操作简单

export function initializeConfig(initConfig) {
    return {
        type: INITIALIZING,
        payload: {
            text: 'Initializing',
            initConfig
        }
    }
}

export function requireMapData() {
    return {
        type: FETCH_DATA_REQUEST,
        payload: {
            text: 'Loading'
        }
    };
}
..........

//Async Action for fetching map data and redraw the map
export function fetchMapData(address) {
    return function (dispatch) {
        //dispatch requireMapData action to set the map in loading state
        dispatch(requireMapData());
        return fetch(address)
            .then(fetchUtil.checkHttpStatus) //check if 404
            .then(fetchUtil.parseJSON)
            .then(mapData => dispatch(fetchDataSucceed(mapData)))
            .catch(error => dispatch(fetchDataFailed(error)));
    }
}

谢谢你我的朋友。

您不能按照您描述的方式复制减速器。两者将以完全相同的方式对完全相同的动作做出响应。

解决方案是让所有热图数据都处于相同的 reducer 状态。例如

const initialState = {
    heatMap1: {},
    heatMap2: {}
};

export default heatmap(state = initialState, action) {
   // etc

现在,如果您想对两个热图使用相同的操作,您将需要一个操作 属性 指定您要定位的堆图。如果您有多个热图,我会推荐一个热图数组,每个操作都包含一个 indexid 以针对特定的热图。例如

function updateHeatMap(index, value) {
    return {
        type: UPDATE_HEATMAP,
        index: index,
        value: value
    }
}

您还可以查看 multireducer 模块 (https://github.com/erikras/multireducer)。它旨在准确解决您提出的方案。

所以您可以这样配置您的商店:

import multireducer from 'multireducer';
import heatMap from './heat-map1'
import {combineReducers} from 'redux';

export let reducers = combineReducers({
  multireducer: multireducer({
    heatMap1: heatMap,
    heatMap2: heatMap
  })
});

之后,您需要使用 connectMultireducer() 而不是 redux 的标准 connect() 来将商店的特定切片连接到特定组件,如下所示:

export default connectMultireducer((state)=> {
    return {
        onState: state.heatMap.onState,
        config: state.heatMap.config
    }
})(CHMSHeatMap)

最后,为了获得每个组件的正确状态部分,您将在渲染它们时传入密钥:

<CHMSHeatMap multireducerKey="heatMap1"/>
<CHMSHeatMap multireducerKey="heatMap2"/>

显然最好阅读 multireducer 存储库中的实际文档,但这应该提供一个简短的概述。基本上,该模块只是抽象了向通过 multireducer 函数创建的每个 reducer 添加基于键的查找的过程。

我建议在没有任何库的情况下使用 multireducer 的原始概念。 基本思想是独特的 Symbol 操作类型和独立的 Redux 模块,如下所示:

import * as services from './../../../api/services';

const initialState = {
  list: [],
};

function getListReducer(state, action) {
  return {
    ...state,
    list: action.payload.list,
  };
}

function removeItemReducer(state, action) {
  const { payload } = action;
  const list = state.list.filter((item, i) => i !== payload.index);
  return {
    ...state,
    list,
  };
}

export default class List {
  constructor() {
    // action types constants
    this.GET_LIST = Symbol('GET_LIST');
    this.REMOVE_ITEM = Symbol('REMOVE_ITEM');
  }
  getList = (serviceName) => {
    return async (dispatch) => {
      const list = await services[serviceName].get();
      dispatch({
        type: this.GET_LIST,
        payload: {
          list,
          serviceName,
        },
      });
    };
  }
  removeItem = (index) => {
    return (dispatch) => {
      dispatch({
        type: this.REMOVE_ITEM,
        payload: {
          index,
        },
      });
    };
  }
  reducer = (state = initialState, action) => {
    switch (action.type) {
      case this.GET_LIST:
        return getListReducer(state, action);

      case this.REMOVE_ITEM:
        return removeItemReducer(state, action);

      default:
        return state;
    }
  }
}

阅读更多信息 there