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

如何解决Redux Toolkit中Promise原生resolve/reject函数序列化失败问题?

解决方案:避免将Promise回调存入Redux

核心问题在于Redux要求状态必须可序列化,而原生Promise的resolve/reject函数无法通过toString()序列化恢复。正确的思路是把回调函数留在组件钩子内部,用唯一ID关联Redux中的弹窗状态,而非尝试序列化函数。

步骤1:调整Redux状态结构

只存储弹窗的配置信息和唯一ID,不涉及任何函数:

// alertSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

export interface AlertState {
  id?: string;
  title?: string;
  confirmLabel: string;
  cancelLabel: string;
}

const initialState: AlertState = {
  confirmLabel: '确认',
  cancelLabel: '取消'
};

const alertSlice = createSlice({
  name: 'alert',
  initialState,
  reducers: {
    setAlert: (state, action: PayloadAction<Omit<AlertState, 'id'> & { id: string }>) => {
      return { ...action.payload };
    },
    closeAlert: () => initialState,
    // 仅用于触发回调的标记action,无需修改状态
    confirmAlert: (_, action: PayloadAction<string>) => {},
    cancelAlert: (_, action: PayloadAction<string>) => {}
  }
});

export const { setAlert, closeAlert, confirmAlert, cancelAlert } = alertSlice.actions;
export const selectAlert = (state: { alert: AlertState }) => state.alert;
export default alertSlice.reducer;

步骤2:重写useConfirm钩子

用useRef维护一个ID到Promise回调的映射表,通过监听Redux的action来触发对应的resolve/reject:

// useConfirm.ts
import { useAppDispatch, useAppSelector } from './store/hooks';
import { setAlert, closeAlert, confirmAlert, cancelAlert, selectAlert } from './alertSlice';
import { useRef, useEffect } from 'react';
import { v4 as uuidv4 } from 'uuid'; // 可替换为自定义ID生成逻辑

export const useConfirm = () => {
  const dispatch = useAppDispatch();
  const alert = useAppSelector(selectAlert);
  // 存储待处理的弹窗回调:key=弹窗ID,value=resolve/reject函数
  const pendingCallbacks = useRef<Record<string, { resolve: () => void; reject: () => void }>>({});

  // 监听确认/取消的action,执行对应回调
  useEffect(() => {
    const handleAction = (action: any) => {
      if (confirmAlert.match(action)) {
        const callback = pendingCallbacks.current[action.payload];
        callback?.resolve();
        delete pendingCallbacks.current[action.payload];
        dispatch(closeAlert());
      } else if (cancelAlert.match(action)) {
        const callback = pendingCallbacks.current[action.payload];
        callback?.reject();
        delete pendingCallbacks.current[action.payload];
        dispatch(closeAlert());
      }
    };

    const unsubscribe = dispatch.subscribe(handleAction);
    return unsubscribe;
  }, [dispatch]);

  const Confirm = async (alertOptions: Omit<AlertState, 'id'>) => {
    return new Promise<boolean>((resolve, reject) => {
      const alertId = uuidv4();
      // 存储回调函数,不传入Redux
      pendingCallbacks.current[alertId] = {
        resolve: () => resolve(true),
        reject: () => resolve(false) // 按需求,取消时返回false
      };
      // 仅将弹窗配置和ID传入Redux
      dispatch(setAlert({ ...alertOptions, id: alertId }));
    });
  };

  return Confirm;
};

步骤3:修改弹窗组件

弹窗组件只需根据Redux状态渲染,并在按钮点击时触发对应的action:

// AlertComponent.tsx
import { useAppDispatch, useAppSelector } from './store/hooks';
import { selectAlert, confirmAlert, cancelAlert } from './alertSlice';
import Button from './Button';

export const AlertComponent = () => {
  const alert = useAppSelector(selectAlert);
  const dispatch = useAppDispatch();

  if (!alert.id) return null;

  return (
    <div className="alert-overlay">
      <div className="alert-content">
        {alert.title && <h3>{alert.title}</h3>}
        <div className="alert-buttons">
          <Button
            onClick={() => dispatch(cancelAlert(alert.id))}
            buttonStyle="ghost"
          >
            {alert.cancelLabel}
          </Button>
          <Button
            onClick={() => dispatch(confirmAlert(alert.id))}
          >
            {alert.confirmLabel}
          </Button>
        </div>
      </div>
    </div>
  );
};

方案优势

  • 完全符合Redux状态可序列化的要求,无需尝试序列化原生函数
  • 回调函数留在钩子内部,避免了序列化/反序列化的问题
  • 通过唯一ID关联弹窗和回调,逻辑清晰且易于维护

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 22:50:40