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

使用Redux Toolkit时localStorage JSON解析报错问题排查

问题分析与修复:Redux Toolkit认证刷新页面JSON解析错误

错误原因

你将纯字符串格式的accessToken存入了localStorage的user键下,但在UserSlice的initialState中使用JSON.parse()解析该值。JSON.parse()仅能处理符合JSON语法的字符串(如带引号的对象/数组),而JWT令牌eyJhbGciOi...是纯字符串,不属于JSON格式,因此刷新页面时解析失败,抛出语法错误。

修复方案

需要调整两处代码,保证localStorage存储格式与解析逻辑一致,同时维持Redux状态与本地存储的数据同步:

1. 修改登录API调用代码

方案A:存储完整用户对象(推荐)

将接口返回的完整用户对象转为JSON字符串存入localStorage,确保后续解析正常:

export const loginUser = async (userData, dispatch) =>{
    dispatch(loginStart())
    try {
        const res = await publicRequest.post('/auth/login', userData)
        // 将完整用户对象转为JSON字符串存储
        localStorage.setItem('user', JSON.stringify(res.data))
        dispatch(loginSuccess(res.data))
    } catch (error) {
        const message = "Wrong Username or Email "
        dispatch(loginFailure(message))
    }
}

方案B:仅存储accessToken

如果只需要存储token,修改localStorage键名(避免与用户对象混淆):

export const loginUser = async (userData, dispatch) =>{
    dispatch(loginStart())
    try {
        const res = await publicRequest.post('/auth/login', userData)
        const accessToken = res.data?.accessToken
        localStorage.setItem('accessToken', accessToken)
        dispatch(loginSuccess(res.data))
    } catch (error) {
        const message = "Wrong Username or Email "
        dispatch(loginFailure(message))
    }
}

2. 修改UserSlice.js代码

对应方案A:存储完整用户对象

补充缺失的状态初始值,同时在登录失败/登出时清空本地存储:

import {createSlice} from '@reduxjs/toolkit'

export const userSlice = createSlice({
  name: 'user',
  initialState : {
    user: JSON.parse(localStorage.getItem("user")) || null,
    isError: false,
    isSuccess: false,
    isLoading: false,
    isFetching: false, // 补充isFetching初始值
    message: ''
  },
  reducers: {
    resetStates: (state) =>{
      state.isLoading = false;
      state.isError = false;
      state.isSuccess = false;
      state.isFetching = false;
      state.message = ""
    },
    loginStart: (state) => {
      state.isFetching = true;
      state.isLoading = true; // 统一加载状态
    },
    loginSuccess: (state, action) => {
      state.isFetching = false;
      state.isLoading = false;
      state.isSuccess = true;
      state.user = action.payload
    },
    loginFailure: (state, action) => {
      state.isLoading = false;
      state.isFetching = false;
      state.isError = true;
      state.message = action.payload
      state.user = null
      localStorage.removeItem('user') // 登录失败清空本地存储
    },
    logoutUser: (state) =>{
      state.user = null
      localStorage.removeItem('user') // 登出时清空本地存储
    }
  }
})

export const { loginStart, loginSuccess, loginFailure, resetStates, logoutUser} = userSlice.actions;
export default userSlice.reducer

对应方案B:仅存储accessToken

调整initialState逻辑,直接获取token字符串,同时维护用户状态与token的同步:

import {createSlice} from '@reduxjs/toolkit'

export const userSlice = createSlice({
  name: 'user',
  initialState : {
    user: null,
    accessToken: localStorage.getItem("accessToken") || null,
    isError: false,
    isSuccess: false,
    isLoading: false,
    isFetching: false,
    message: ''
  },
  reducers: {
    resetStates: (state) =>{
      state.isLoading = false;
      state.isError = false;
      state.isSuccess = false;
      state.isFetching = false;
      state.message = ""
    },
    loginStart: (state) => {
      state.isFetching = true;
      state.isLoading = true;
    },
    loginSuccess: (state, action) => {
      state.isFetching = false;
      state.isLoading = false;
      state.isSuccess = true;
      state.user = action.payload
      state.accessToken = action.payload.accessToken
    },
    loginFailure: (state, action) => {
      state.isLoading = false;
      state.isFetching = false;
      state.isError = true;
      state.message = action.payload
      state.user = null
      state.accessToken = null
      localStorage.removeItem('accessToken')
    },
    logoutUser: (state) =>{
      state.user = null
      state.accessToken = null
      localStorage.removeItem('accessToken')
    }
  }
})

export const { loginStart, loginSuccess, loginFailure, resetStates, logoutUser} = userSlice.actions;
export default userSlice.reducer

额外提示:你的slice中同时使用了isLoading和isFetching两个状态,建议统一状态命名或补充完整初始值,避免状态不一致问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 10:45:47