使用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
相关产品推荐
相关产品推荐

