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

NextJS中Redux+Web3Auth状态突变错误排查与解决求助

问题

在Next.js中使用Redux和Web3Auth开发应用时,偶发状态突变错误,此前在React.js中运行正常。错误出现时会阻断流程,无规律可循。

报错信息

Error: Invariant failed: A state mutation was detected between dispatches, in the path 'web3authInfo.web3authInstance.walletAdapters.openlogin.openloginInstance.currentStorage.storage.persist:nextjs'. This may cause incorrect behavior.

错误触发于connect函数的catch块,函数代码如下:

const connect = async () => {
    console.log("Hello");
    setIsLoading(true);
    try {
      const web3auth = new Web3Auth({
        clientId: process.env.CLIENT_ID,
        chainConfig: {
          chainNamespace: "eip155",
          chainId: "0x5", 
        },
        web3AuthNetwork: "mainnet",
      });

      await web3auth.initModal();

      await web3auth.connect();
      const user = await web3auth.getUserInfo();

      const isEmpty = Object.keys(user).length === 0;
      console.log(user, " user", isEmpty);

      if (isEmpty) {
        console.log("no user");
        setIsLoading(false);
        await handleWeb3Logout(web3auth, dispatch);
      }

      const app_scoped_privkey = await web3auth.provider?.request({
        method: "eth_private_key", 
      });
      console.log(app_scoped_privkey, " app scoped private key");
      saveWeb3Instance(dispatch, web3auth);
      if (typeof app_scoped_privkey === "string") {
        const app_pub_key = getPublicCompressed(
          Buffer.from(app_scoped_privkey.padStart(64, "0"), "hex")
        ).toString("hex");
        console.log(app_pub_key, app_scoped_privkey, " apppppp key");

        console.log(user.idToken, " id token id token", user);

        const postRes = await web3authApi(
          dispatch,
          setStatusCode,
          setEmail,
          app_pub_key,
          user.idToken,
          web3authInfo.web3authInstance
        );
      }
    } catch (err) {
      console.log("connect error", err);
    }
  };

相关Redux代码

web3reducer.js

const initialState = {
  web3authInstance: null,
  walletAddress: null,
};

export const web3authReducer = (state = initialState, action) => {
  switch (action.type) {
    case "SET_WEB3AUTH_INSTANCE":
      return { ...state, web3authInstance: action.payload };
    case "SET_WALLET_ADDRESS":
      return { ...state, walletAddress: action.payload };
    case "DISCONNECT_WEB3AUTH":
      return {
        ...state,
        web3authInstance: null,
        walletAddress: null,
      };
    default:
      return state;
  }
};

web3action.js

import { RESPONSE_MESSAGE } from "../../utils/message";
import { getSigner } from "@src/function/web3Functions";

export const setWeb3AuthInstance = (web3authInstance) => {
  return async (dispatch) => {
    try {
      dispatch({
        type: "SET_WEB3AUTH_INSTANCE",
        payload: web3authInstance,
      });
    } catch (error) {
      console.error("Error from redux web3authInstance:", error);
    }
  };
};

export const setWalletAddress = (web3authInstance) => {
  return async (dispatch) => {
    try {
      const signer = await getSigner(web3authInstance);
      const walletAddress = await signer.getAddress();

      dispatch({
        type: "SET_WALLET_ADDRESS",
        payload: walletAddress,
      });
    } catch (error) {
      console.error("Error from redux wallet address:", error);
    }
  };
};

export const disconnectWeb3Auth = () => {
  return {
    type: "DISCONNECT_WEB3AUTH",
  };
};

解决方案

1. 避免将完整Web3Auth实例存入Redux

Redux要求状态不可变,但Web3Auth实例是带内部状态的可变对象,其内部存储模块会在运行中自动修改自身状态,触发Redux的突变检测。

修改方案:

  • 仅在Redux中存储必要的序列化数据(如钱包地址、用户信息、token等),不存储完整实例。
  • 将Web3Auth实例存在组件的useRef中:
// 组件内初始化ref
const web3authRef = useRef(null);

const connect = async () => {
  // ...其他代码
  const web3auth = new Web3Auth({/* config */});
  await web3auth.initModal();
  await web3auth.connect();
  web3authRef.current = web3auth; // 存入ref
  // 仅将需要的数据存入Redux
  dispatch(setWalletAddress(web3auth));
  // ...其他代码
};

2. 深拷贝实例后存入Redux(临时应急方案)

若必须存入Redux,可在dispatch前对实例做深拷贝,切断引用,但此方法可能导致Web3Auth功能异常(拷贝后的实例可能丢失内部绑定方法):

// 修改setWeb3AuthInstance action
export const setWeb3AuthInstance = (web3authInstance) => {
  return async (dispatch) => {
    try {
      const clonedInstance = JSON.parse(JSON.stringify(web3authInstance));
      dispatch({
        type: "SET_WEB3AUTH_INSTANCE",
        payload: clonedInstance,
      });
    } catch (error) {
      console.error("Error from redux web3authInstance:", error);
    }
  };
};

注意:此方案仅作临时过渡,优先选择方案1。

3. 适配Next.js的SSR环境

Web3Auth需在客户端环境初始化,确保初始化逻辑仅在浏览器执行:

const connect = async () => {
  if (typeof window === 'undefined') return;
  // ...后续Web3Auth初始化代码
};

4. 关闭Redux严格模式(不推荐)

Next.js默认启用React严格模式,Redux在该模式下会加倍检测状态突变。可在next.config.js中关闭,但这会掩盖潜在问题:

// next.config.js
module.exports = {
  reactStrictMode: false,
};

不推荐此方案,严格模式有助于提前发现隐性bug。


内容的提问来源于stack exchange,提问作者jakia.amin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 23:03:15