如何在非函数组件的LoginRequest中使用React Hooks(useDispatch)?
问题分析
核心业务函数LoginRequest直接调用useDispatch违反React Hooks规则(Hooks仅允许在函数组件或自定义Hooks的顶层调用),该函数被APP多处依赖,需要调整Token存储逻辑,同时可结合axios拦截器实现Token的自动管理。
解决方案
方案1:传入dispatch参数(最小改动)
直接将dispatch作为参数传入LoginRequest,避免在非组件/非自定义Hooks中调用Hooks:
修改后的LoginRequest:
export async function LoginRequest(chiperText, dispatch) { // 新增dispatch参数 try { let loginRequest = await fetch(baseUrl + "/Login", { method: "post", body: JSON.stringify({ chiperText }), headers: { "Content-Type": "application/json-patch+json", Accept: "application/json", }, }); let jwtToken = await loginRequest.json(); dispatch(setJwtToken(jwtToken)); // 使用传入的dispatch return jwtToken; } catch (error) { console.error(error); return null; } }
在调用的函数组件中使用:
import { useDispatch } from 'react-redux'; import { LoginRequest, GetLoginChiperText } from './你的文件路径'; function LoginScreen() { const dispatch = useDispatch(); const handleLogin = async () => { const chiperText = GetLoginChiperText({/* 传入参数 */}); const token = await LoginRequest(chiperText, dispatch); // 后续业务逻辑 }; return <>{/* 组件内容 */}</>; }
方案2:直接使用Redux Store实例
在Redux Store配置文件中导出Store实例,工具函数直接调用store.dispatch:
- 配置Store并导出(示例
store.js):
import { createStore } from 'redux'; import rootReducer from './reducers'; export const store = createStore(rootReducer); // 导出Store实例
- 修改
LoginRequest:
// 导入Store实例 import { store } from '../store'; // 路径根据实际项目调整 import { setJwtToken } from "../actions/jwtTokenActions"; export async function LoginRequest(chiperText) { try { let loginRequest = await fetch(baseUrl + "/Login", { method: "post", body: JSON.stringify({ chiperText }), headers: { "Content-Type": "application/json-patch+json", Accept: "application/json", }, }); let jwtToken = await loginRequest.json(); store.dispatch(setJwtToken(jwtToken)); // 直接调用Store的dispatch return jwtToken; } catch (error) { console.error(error); return null; } }
方案3:结合Axios拦截器自动管理Token(推荐)
通过axios拦截器统一处理请求头的Token携带、Token过期重试等场景,同时整合Token存储逻辑:
步骤1:创建Axios实例并配置拦截器
新建apiClient.js文件:
import axios from 'axios'; import Constants from "expo-constants"; import { store } from '../store'; import { setJwtToken } from "../actions/jwtTokenActions"; const baseUrl = Constants.manifest.configurations.baseUrl; // 创建axios实例 const apiClient = axios.create({ baseURL: baseUrl, headers: { "Content-Type": "application/json-patch+json", Accept: "application/json", }, }); // 请求拦截器:自动为请求添加Authorization头 apiClient.interceptors.request.use( (config) => { const state = store.getState(); const jwtToken = state.jwtToken; // 对应Redux中存储Token的字段 if (jwtToken) { config.headers.Authorization = `Bearer ${jwtToken}`; } return config; }, (error) => Promise.reject(error) ); // 响应拦截器:处理Token过期等异常(可选) apiClient.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; // 假设后端返回401代表Token过期 if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; // 此处可添加Token刷新逻辑,示例: // const newToken = await refreshToken(); // store.dispatch(setJwtToken(newToken)); // originalRequest.headers.Authorization = `Bearer ${newToken}`; // return apiClient(originalRequest); } return Promise.reject(error); } ); export default apiClient;
步骤2:修改业务函数使用Axios实例
import apiClient from './apiClient'; import { store } from '../store'; import { setJwtToken } from "../actions/jwtTokenActions"; export async function LoginRequest(chiperText) { try { const { data: jwtToken } = await apiClient.post("/api/Login/Login", { chiperText }); store.dispatch(setJwtToken(jwtToken)); return jwtToken; } catch (error) { console.error(error); return null; } } // 简化CheckJwtToken函数 export async function CheckJwtToken() { try { await apiClient.get("/api/Login/CheckJwtToken"); return true; } catch (error) { console.log(error) return false; } }
步骤3:修复GetLoginChiperText的变量错误
原函数中误用body变量,替换为参数params:
export function GetLoginChiperText(params) { params.time = getCurrentUtcDate().getTime(); var key = enc.Utf8.parse(secretKey); var iv = enc.Utf8.parse(secretKey); var chiperText = AES.encrypt(enc.Utf8.parse(JSON.stringify(params)), key, { keySize: 128 / 8, iv: iv, mode: mode.CBC, padding: pad.Pkcs7, }).toString(); return chiperText; }
方案选择
- 快速修复现有问题选方案1,改动最小;
- 希望工具函数直接操作Redux选方案2;
- 长期维护、统一管理API请求选方案3,便于后续扩展Token刷新、请求重试等功能。
内容的提问来源于stack exchange,提问作者Joss
相关产品推荐
相关产品推荐

