You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redux如何设计actions与reducer实现同页面两API数据独立渲染

问题场景

单页面内包含2个图表组件,两个图表的数据源来自相互独立的API,但共用页面上的同一套UI筛选逻辑:用户在筛选区选定参数后,系统会携带相同的筛选查询参数分别向两个API发起请求,两个接口返回的各自数据需要分别渲染到对应图表中。

现存问题

现有实现中两个接口请求的actions复用了同一套ActionTypes,reducer仅维护单一data字段存储接口数据,导致后返回的接口数据会覆盖先返回的数据,从store中读取的图表数据会被异常篡改,无法实现两个图表数据的独立管理,同时需要完整保留原有的加载中、请求错误、超时等状态处理能力。

原有问题代码如下:

actions.js(原实现)

export const getSeverityDistData = (dispatch, urlqueryStr) => {
handleFetch(
    {
        ...getSeverityDist(),
        urlqueryStr,
    }, // fetch params
    (res) => {
        dispatch({
            type: ActionTypes.GET_DASHBOARD_LIST_SUCCESS,
            payload: {
                data: res.data,
                isLoading: false,
                onError: false,
            },
        });
    },
    (e) => {
        dispatch({
            type: ActionTypes.GET_DASHBOARD_LIST_ERROR,
            payload: {
                errorMsg: e.message,
                isLoading: false,
            },
        });
    },
    (loading) => {
        dispatch({
            type: ActionTypes.GET_DASHBOARD_LIST_LOADING,
            payload: loading,
        });
    }
);};
export const getGroupData = (dispatch, urlqueryStr) => {
handleFetch(
{
    ...getGruop(),
    urlqueryStr,
},//...sameOperations SameActionTypes...}

reducers.js(原实现)

const setChartsData = (state, action) => ({
    ...state,
    data: [...action.payload.data],
    isLoading: action.payload.isLoading,
    onError: action.payload.onError,
});

const setLoading = (state, action) => ({
    ...state,
    isLoading: action.payload,
});

const setTimeOut = (state, action) => ({
    ...state,
    isTimeout: action.payload,
});

const onError = (state, action) => ({
    ...state,
    onError: action.payload.errorMsg,
    isLoading: action.payload.isLoading,
});

const initialState = {
    data: [],
    isLoading: false,
    isTimeout: false,
    onError: '',
};

export default (state = initialState, action) => {
    switch (action.type) {
        case ActionTypes.GET_DASHBOARD_LIST_SUCCESS:
            return setChartsData(state, action);
        case ActionTypes.GET_DASHBOARD_LIST_LOADING:
            return setLoading(state, action);
        case ActionTypes.GET_DASHBOARD_LIST_TIME_OUT:
            return setTimeOut(state, action);
        case ActionTypes.GET_DASHBOARD_LIST_ERROR:
            return onError(state, action);
        default:
            return state;
    }
};
实现方案

核心思路是按图表维度拆分独立状态切片,在action payload中增加唯一图表标识字段区分请求归属,无需完全重写原有逻辑,改动成本极低。

Action层改造

首先定义图表唯一标识常量,避免硬编码导致的匹配错误;给两个接口的所有dispatch动作都加上对应图表标识,原有ActionTypes可直接复用,无需新增重复的类型定义。

// 图表标识常量,后续新增图表直接在这里追加即可
export const CHART_KEYS = {
  SEVERITY_DIST: 'severityDist',
  GROUP: 'group'
}

export const getSeverityDistData = (dispatch, urlqueryStr) => {
  const chartKey = CHART_KEYS.SEVERITY_DIST
  handleFetch(
    {
      ...getSeverityDist(),
      urlqueryStr,
    },
    (res) => {
      dispatch({
        type: ActionTypes.GET_DASHBOARD_LIST_SUCCESS,
        payload: {
          chartKey,
          data: res.data,
          isLoading: false,
          onError: false,
        },
      });
    },
    (e) => {
      dispatch({
        type: ActionTypes.GET_DASHBOARD_LIST_ERROR,
        payload: {
          chartKey,
          errorMsg: e.message,
          isLoading: false,
        },
      });
    },
    (loading) => {
      dispatch({
        type: ActionTypes.GET_DASHBOARD_LIST_LOADING,
        payload: {
          chartKey,
          loading
        },
      });
    },
    (timeout) => {
      dispatch({
        type: ActionTypes.GET_DASHBOARD_LIST_TIME_OUT,
        payload: {
          chartKey,
          isTimeout: timeout
        }
      })
    }
  );
};

export const getGroupData = (dispatch, urlqueryStr) => {
  const chartKey = CHART_KEYS.GROUP
  handleFetch(
    {
      ...getGruop(),
      urlqueryStr,
    },
    (res) => {
      dispatch({
        type: ActionTypes.GET_DASHBOARD_LIST_SUCCESS,
        payload: {
          chartKey,
          data: res.data,
          isLoading: false,
          onError: false,
        },
      });
    },
    (e) => {
      dispatch({
        type: ActionTypes.GET_DASHBOARD_LIST_ERROR,
        payload: {
          chartKey,
          errorMsg: e.message,
          isLoading: false,
        },
      });
    },
    (loading) => {
      dispatch({
        type: ActionTypes.GET_DASHBOARD_LIST_LOADING,
        payload: {
          chartKey,
          loading
        },
      });
    },
    (timeout) => {
      dispatch({
        type: ActionTypes.GET_DASHBOARD_LIST_TIME_OUT,
        payload: {
          chartKey,
          isTimeout: timeout
        }
      })
    }
  );
};

Reducer层改造

将原有单一根状态调整为按chartKey存储独立状态的结构,每个图表对应独立的data、isLoading、isTimeout、onError字段,更新状态时仅修改对应chartKey下的属性,从根本上避免不同接口数据互相覆盖的问题。

import { CHART_KEYS } from './actions'

// 单个图表的初始状态生成函数
const createChartInitialState = () => ({
  data: [],
  isLoading: false,
  isTimeout: false,
  onError: '',
})

const initialState = {
  [CHART_KEYS.SEVERITY_DIST]: createChartInitialState(),
  [CHART_KEYS.GROUP]: createChartInitialState(),
};

export default (state = initialState, action) => {
  const { chartKey } = action.payload || {}
  // 无有效chartKey的动作直接返回原状态,兼容其他全局逻辑
  if (!chartKey || !state[chartKey]) return state

  switch (action.type) {
    case ActionTypes.GET_DASHBOARD_LIST_SUCCESS:
      return {
        ...state,
        [chartKey]: {
          ...state[chartKey],
          data: [...action.payload.data],
          isLoading: action.payload.isLoading,
          onError: action.payload.onError,
          isTimeout: false
        }
      };
    case ActionTypes.GET_DASHBOARD_LIST_LOADING:
      return {
        ...state,
        [chartKey]: {
          ...state[chartKey],
          isLoading: action.payload.loading,
          // 发起新请求时清空历史错误、超时状态
          onError: '',
          isTimeout: false
        }
      };
    case ActionTypes.GET_DASHBOARD_LIST_TIME_OUT:
      return {
        ...state,
        [chartKey]: {
          ...state[chartKey],
          isTimeout: action.payload.isTimeout,
          isLoading: false
        }
      };
    case ActionTypes.GET_DASHBOARD_LIST_ERROR:
      return {
        ...state,
        [chartKey]: {
          ...state[chartKey],
          onError: action.payload.errorMsg,
          isLoading: action.payload.isLoading,
          isTimeout: false
        }
      };
    default:
      return state;
  }
};

组件侧适配

两个图表组件从store取数时,仅读取对应chartKey下的状态即可:

  • 严重程度分布图表读取state.dashboard[CHART_KEYS.SEVERITY_DIST]下的data、isLoading等状态
  • 分组统计图表读取state.dashboard[CHART_KEYS.GROUP]下的对应状态

方案优势

  • 无需新增多套重复的ActionTypes,避免冗余代码
  • 扩展性强,后续新增同页面其他图表时,仅需在CHART_KEYS中新增对应常量、补充接口调用逻辑即可,无需修改reducer核心逻辑
  • 每个图表独立维护加载、错误、超时状态,不会出现A接口请求完成就覆盖B接口数据的问题,也不会出现单个接口加载完成就关闭所有图表加载态的异常
  • 原有筛选区触发请求的逻辑无需改动,筛选参数变更时并行调用两个数据请求action即可

内容的提问来源于stack exchange,提问作者Taha Yıldırım

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 21:45:33