
本教程旨在解决 react-redux 应用中未登录用户不必要地请求用户数据和敏感 api key 导致 401 错误的问题。我们将通过在 redux thunk 中引入认证状态检查机制,并结合组件层面的状态判断,实现用户数据的按需加载,从而优化应用性能并提升用户体验。
在构建基于 React 和 Redux 的认证应用时,一个常见的挑战是如何确保只有在用户登录后才尝试加载其专属数据或需要认证的敏感资源。如果应用在初始化时无条件地发起这些请求,未登录用户将会收到大量的 401 Unauthorized 错误,这不仅会污染开发者工具的控制台,还会造成不必要的网络请求,影响应用性能。
根据提供的代码示例,问题主要来源于 App.js 中的 useEffect 钩子:
loadUser action 内部通过 axios.get('/api/profile') 尝试获取用户资料。如果用户当前没有有效的认证会话(例如,尚未登录或认证 token 已过期),后端 API 会拒绝这些请求并返回 401 状态码,从而导致前端出现错误。
为了解决这个问题,核心思想是在发起这些需要认证的请求之前,先检查用户的认证状态。我们可以通过以下两种主要方式实现:
本教程将结合这两种方法:loadUser 动作在 Thunk 内部进行认证检查,而 Stripe API Key 的请求则在 App.js 中基于 Redux 状态进行判断。
首先,我们需要修改 loadUser Redux Thunk,引入 getState 参数来访问 Redux store 的当前状态。在发起 API 请求之前,检查 getState().auth.isAuthenticated(假设 auth reducer 中有一个 isAuthenticated 字段表示用户是否登录)。
// actions/user.js
import axios from 'axios';
export const loadUser = () => async (dispatch, getState) => {
// 在尝试加载用户之前,检查用户是否已认证
// 假设 Redux 状态中有一个 auth.isAuthenticated 字段
if (!getState().auth.isAuthenticated) {
// 如果用户未认证,则直接返回,不执行后续的 API 请求
// 这样可以避免在未登录状态下发起不必要的 /api/profile 请求
return;
}
try {
dispatch({ type: 'LOAD_USER_REQ' });
// 注意:根据您的代理配置,API 路径可能是 '/api/profile'
const { data } = await axios.get('/api/profile');
dispatch({
type: 'LOAD_USER_SUCCESS',
payload: data.user
});
} catch (error) {
// 即使进行了预检查,API 请求仍可能因其他原因失败(如 token 过期),
// 此时应处理错误,并将认证状态设为 false。
dispatch({
type: 'LOAD_USER_FAIL',
payload: error.response && error.response.data.message
? error.response.data.message
: error.message
});
}
};
export const clearErrors = () => async dispatch => {
dispatch({ type: 'CLEAR_ERRORS' });
};对应的 Reducer 状态管理:
为了使上述检查生效,authReducer 必须正确地管理 isAuthenticated 状态。isAuthenticated 应该在用户成功登录/注册后设为 true,在登出或加载用户失败时设为 false。
// reducers/user.js
const initialState = {
user: null, // 初始用户数据设为 null
isAuthenticated: false, // 初始认证状态为 false
loading: false,
error: null
};
export const authReducer = (state = initialState, action) => {
switch (action.type) {
case 'LOAD_USER_REQ':
return {
...state,
loading: true,
error: null
};
case 'LOAD_USER_SUCCESS':
return {
...state,
loading: false,
isAuthenticated: true,
user: action.payload,
error: null
};
case 'LOAD_USER_FAIL':
return {
...state,
loading: false,
isAuthenticated: false, // 加载失败,视为未认证
user: null, // 清空用户数据
error: action.payload
};
// 假设您有登录/注册/登出相关的 action
// case 'LOGIN_SUCCESS':
// case 'REGISTER_SUCCESS':
// return { ...state, loading: false, isAuthenticated: true, user: action.payload, error: null };
// case 'LOGOUT_SUCCESS':
// return { ...state, loading: false, isAuthenticated: false, user: null, error: null };
case 'CLEAR_ERRORS':
return {
...state,
error: null
};
default:
return state;
}
};对于 Stripe API Key,由于它在 App.js 的 useEffect 中直接通过 axios 请求,我们可以利用 useSelector 从 Redux store 中获取认证状态,然后有条件地执行请求。
// App.js
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { Elements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
import axios from 'axios';
import { useSelector } from 'react-redux'; // 导入 useSelector
import Header from './components/Header';
import Home from './components/Home';
import Payment from './components/Payment';
import Profile from './components/Profile';
import { loadUser } from './actions/user';
import store from './store';
export default function App() {
const [stripeApiKey, setStripeApiKey] = useState('');
// 从 Redux store 中获取认证状态
const { isAuthenticated } = useSelector(state => state.auth);
useEffect(() => {
// 无论用户是否认证,都尝试 dispatch loadUser。
// loadUser thunk 内部会根据 isAuthenticated 状态决定是否发起 API 请求。
store.dispatch(loadUser());
// 只有当用户已认证时才请求 Stripe API Key
if (isAuthenticated) {
(async () => {
try {
const { data } = await axios.get('/api/stripeapikey');
setStripeApiKey(data.stripeApiKey);
} catch (error) {
console.error('Failed to load Stripe API Key:', error);
// 可以在此添加错误处理,例如 dispatch 一个 action 来更新 Redux 状态
}
})();
} else {
// 如果用户未认证,确保清空或重置 stripeApiKey
setStripeApiKey('');
}
}, [isAuthenticated]); // 依赖 isAuthenticated,当认证状态改变时重新运行此 effect
return (
<Router>
<div className="App">
<Header />
<Routes>
<Route path="/" element={<Home />} />
{/* 这些路由可能需要额外的路由保护以上就是React-Redux 应用中实现用户数据的条件加载的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号