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

React组件中多Redux异步操作的优化实现方案咨询

优化Redux Toolkit异步操作的组件使用方式

方案1:在Slice中拆分各异步操作的状态处理

把每个异步操作的状态(pending/succeeded/failed)和对应数据单独维护在Slice里,让Slice负责数据更新逻辑,组件只专注UI渲染。这样新增DELETE、PUT时,只需要在Slice里扩展逻辑,不用在组件里堆判断。

示例Slice代码:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import Services from './your-services-path';
import type { IGetAllAnnotationType, ICreateAnnotationType } from './your-types-path';

// 已有的异步Action
export const getAllAnnotationType = createAsyncThunk<IGetAllAnnotationType>(
  "annotationType/getAll",
  async (_, { rejectWithValue }) => {
    try {
      const response = await Services.get("/globalAnnotationTypes");
      return response.data.slice().reverse() as IGetAllAnnotationType;
    } catch (err) {
      const error = err as any;
      return rejectWithValue(error?.response?.data);
    }
  }
);

export const createAnnotationType = createAsyncThunk<
  ICreateAnnotationType,
  { name: string; des: string }
>("annotationType/create", async ({ name, des }, { rejectWithValue }) => {
  try {
    const response = await Services.post("/globalAnnotationTypes", { name, des });
    return response.data as ICreateAnnotationType;
  } catch (err) {
    const error = err as any;
    return rejectWithValue(error?.response?.data);
  }
});

// 后续新增的DELETE、PUT Action示例
export const deleteAnnotationType = createAsyncThunk<string, string>(
  "annotationType/delete",
  async (id, { rejectWithValue }) => {
    try {
      await Services.delete(`/globalAnnotationTypes/${id}`);
      return id;
    } catch (err) {
      const error = err as any;
      return rejectWithValue(error?.response?.data);
    }
  }
);

export const updateAnnotationType = createAsyncThunk<
  ICreateAnnotationType,
  { id: string; name: string; des: string }
>("annotationType/update", async ({ id, name, des }, { rejectWithValue }) => {
  try {
    const response = await Services.put(`/globalAnnotationTypes/${id}`, { name, des });
    return response.data as ICreateAnnotationType;
  } catch (err) {
    const error = err as any;
    return rejectWithValue(error?.response?.data);
  }
});

const annotationTypeSlice = createSlice({
  name: "annotationType",
  initialState: {
    list: [] as IGetAllAnnotationType,
    getAllStatus: "idle" as "idle" | "pending" | "succeeded" | "failed",
    createStatus: "idle" as "idle" | "pending" | "succeeded" | "failed",
    deleteStatus: "idle" as "idle" | "pending" | "succeeded" | "failed",
    updateStatus: "idle" as "idle" | "pending" | "succeeded" | "failed",
    error: null as any,
    lastCreated: null as ICreateAnnotationType | null,
  },
  reducers: {
    resetAnnotationType: (state) => {
      state.lastCreated = null;
      state.error = null;
    },
  },
  extraReducers: (builder) => {
    builder
      // 处理getAll逻辑
      .addCase(getAllAnnotationType.pending, (state) => {
        state.getAllStatus = "pending";
      })
      .addCase(getAllAnnotationType.succeeded, (state, action) => {
        state.getAllStatus = "succeeded";
        state.list = action.payload;
        state.error = null;
      })
      .addCase(getAllAnnotationType.failed, (state, action) => {
        state.getAllStatus = "failed";
        state.error = action.payload;
      })
      // 处理create逻辑
      .addCase(createAnnotationType.pending, (state) => {
        state.createStatus = "pending";
      })
      .addCase(createAnnotationType.succeeded, (state, action) => {
        state.createStatus = "succeeded";
        state.lastCreated = action.payload;
        state.error = null;
        // 直接在Slice里更新列表,不用组件再触发getAll
        state.list = [action.payload, ...state.list];
      })
      .addCase(createAnnotationType.failed, (state, action) => {
        state.createStatus = "failed";
        state.error = action.payload;
      })
      // 处理delete逻辑
      .addCase(deleteAnnotationType.pending, (state) => {
        state.deleteStatus = "pending";
      })
      .addCase(deleteAnnotationType.succeeded, (state, action) => {
        state.deleteStatus = "succeeded";
        state.list = state.list.filter(item => item.id !== action.payload);
        state.error = null;
      })
      .addCase(deleteAnnotationType.failed, (state, action) => {
        state.deleteStatus = "failed";
        state.error = action.payload;
      })
      // 处理update逻辑
      .addCase(updateAnnotationType.pending, (state) => {
        state.updateStatus = "pending";
      })
      .addCase(updateAnnotationType.succeeded, (state, action) => {
        state.updateStatus = "succeeded";
        state.list = state.list.map(item => 
          item.id === action.payload.id ? action.payload : item
        );
        state.error = null;
      })
      .addCase(updateAnnotationType.failed, (state, action) => {
        state.updateStatus = "failed";
        state.error = action.payload;
      });
  },
});

export const { resetAnnotationType } = annotationTypeSlice.actions;
export default annotationTypeSlice.reducer;

方案2:组件中单独监听各异步操作的状态

组件不再用统一的typeLoader,而是分别获取每个操作的状态,逻辑拆分清晰,新增操作时只需添加对应状态的监听即可:

import { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { getAllAnnotationType, createAnnotationType, resetAnnotationType } from './annotationTypeSlice';
import type { RootState } from './store';

const AnnotationTypeComponent = () => {
  const dispatch = useDispatch();
  const { 
    list: getAllData, 
    getAllStatus, 
    createStatus, 
    lastCreated,
    error 
  } = useSelector((state: RootState) => state.annotationType);
  const [rows, setRows] = useState(getAllData);
  const [loader, setLoader] = useState(false);
  const [noticeMsg, setNoticeMsg] = useState(null);

  // 初始加载数据
  useEffect(() => {
    dispatch(getAllAnnotationType());
  }, [dispatch]);

  // 监听getAll状态变化
  useEffect(() => {
    switch (getAllStatus) {
      case 'pending':
        setLoader(true);
        break;
      case 'succeeded':
        setLoader(false);
        setRows(getAllData);
        break;
      case 'failed':
        showToast(error);
        setLoader(false);
        break;
    }
  }, [getAllStatus, getAllData, error]);

  // 监听create状态变化
  useEffect(() => {
    if (createStatus === 'succeeded' && lastCreated) {
      showToast();
      setNoticeMsg(lastCreated);
      dispatch(resetAnnotationType());
    } else if (createStatus === 'failed') {
      showToast(error);
      dispatch(resetAnnotationType());
    }
  }, [createStatus, lastCreated, error, dispatch]);

  // 后续新增delete/update时,直接加对应的useEffect即可
  // 示例:监听delete状态
  // useEffect(() => {
  //   if (deleteStatus === 'succeeded') {
  //     showToast('删除成功');
  //   } else if (deleteStatus === 'failed') {
  //     showToast(error);
  //   }
  // }, [deleteStatus, error]);

  // 触发创建操作
  const handleCreate = (name: string, des: string) => {
    dispatch(createAnnotationType({ name, des }));
  };

  return (
    // 组件UI内容
    <button onClick={() => handleCreate('新类型', '测试描述')} disabled={createStatus === 'pending'}>
      创建标注类型
    </button>
  );
};

export default AnnotationTypeComponent;

方案3:抽离通用逻辑到自定义Hook

如果多个组件都需要处理类似的异步操作,可以把通用逻辑抽成自定义Hook,减少重复代码:

import { useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';

export const useAsyncAction = <T>(
  actionCreator: (...args: any[]) => any,
  onSuccess?: (data: T) => void,
  onError?: (error: any) => void
) => {
  const dispatch = useDispatch();
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState<T | null>(null);
  const [error, setError] = useState<any>(null);

  const execute = async (...args: any[]) => {
    setLoading(true);
    try {
      // 用unwrapResult直接获取成功数据或捕获错误
      const result = await dispatch(actionCreator(...args)).unwrap();
      setData(result);
      onSuccess?.(result);
    } catch (err) {
      setError(err);
      onError?.(err);
    } finally {
      setLoading(false);
    }
  };

  return { execute, loading, data, error };
};

组件中使用这个Hook:

import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { getAllAnnotationType, createAnnotationType } from './annotationTypeSlice';
import { useAsyncAction } from './useAsyncAction';
import type { RootState, IGetAllAnnotationType, ICreateAnnotationType } from './types';

const AnnotationTypeComponent = () => {
  const { list: initialData } = useSelector((state: RootState) => state.annotationType);
  const [rows, setRows] = useState(initialData);
  const [noticeMsg, setNoticeMsg] = useState<ICreateAnnotationType | null>(null);

  // 处理getAll操作
  const { 
    execute: fetchAllTypes, 
    loading: getAllLoading, 
    data: getAllData 
  } = useAsyncAction<IGetAllAnnotationType>(
    getAllAnnotationType,
    (data) => setRows(data)
  );

  // 处理create操作
  const { 
    execute: createType, 
    loading: createLoading 
  } = useAsyncAction<ICreateAnnotationType>(
    createAnnotationType,
    (data) => {
      showToast();
      setNoticeMsg(data);
      fetchAllTypes(); // 创建成功后自动拉取最新列表
    },
    (error) => showToast(error)
  );

  // 初始加载
  useEffect(() => {
    fetchAllTypes();
  }, [fetchAllTypes]);

  // 合并所有loading状态
  const loader = getAllLoading || createLoading;

  return (
    <div>
      <button onClick={() => createType('新类型', '测试描述')} disabled={createLoading}>
        创建标注类型
      </button>
      {/* 其他UI内容 */}
    </div>
  );
};

核心优化思路

  • 把异步操作的状态管理从组件移到Slice,让Slice负责数据更新,组件只做UI渲染和事件触发。
  • 每个异步操作单独维护状态,避免用一个统一状态判断所有操作,导致逻辑冗余。
  • 利用unwrapResult简化异步操作的结果和错误处理。
  • 抽离自定义Hook复用通用逻辑,减少组件代码量。

内容的提问来源于stack exchange,提问作者Nikhil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 22:10:44