如何让自定义useAuth Hook跳过初始null,仅返回认证状态更新后的值
结论
不存在可以跳过初始阶段直接拿到请求后状态的方案,这是异步网络请求的固有特性,和你使用useEffect还是useLayoutEffect无关:
- React 函数组件和自定义 Hook 的首次渲染是同步执行的,没有办法阻塞渲染流程等待异步请求返回结果
- 网络请求的响应属于JS事件循环中的异步任务,必须等当前同步执行的所有代码(包括Hook的首次返回)执行完成后才会被处理,因此首次渲染必然返回你设置的初始值
null
推荐解决方案
方案1:补充加载状态,上层组件适配
修改Hook的返回结构,额外返回加载状态,让调用方自行处理加载中的展示逻辑,修改后的代码如下:
export function useAuth() { const [state, setState] = useState({ isAuthenticated: null, loading: true }); useEffect(() => { let mounted = true; axios({ method: 'GET', url: 'http://localhost:8000/api/still-authenticated/', headers: { 'Content-type': 'application/json', }, withCredentials: true, }) .then((res) => { if (mounted) { setState({ isAuthenticated: res.data.isAuthenticated, loading: false }); } }) .catch(() => { // 处理请求失败的场景,默认认为未登录 if (mounted) { setState({ isAuthenticated: false, loading: false }); } }); return () => mounted = false; },[setState]) return state; }
调用示例:
const Page = () => { const { loading, isAuthenticated } = useAuth(); // 加载阶段渲染骨架屏/加载动画 if (loading) return <div>加载中...</div> // 加载完成后根据认证状态渲染对应内容 return isAuthenticated ? <UserDashboard /> : <LoginPage /> }
方案2:本地缓存认证状态,减少等待时间
你可以把认证状态提前存在localStorage/sessionStorage或者全局状态管理库中,Hook初始化时先读取本地缓存作为初始值,再发请求和服务端校验同步最新状态:
export function useAuth() { // 初始化优先读取本地缓存的认证状态 const [isAuthenticated, setIsAuthenticated] = useState(() => { return localStorage.getItem('isAuthenticated') === 'true'; }); useEffect(() => { let mounted = true; axios({ method: 'GET', url: 'http://localhost:8000/api/still-authenticated/', headers: { 'Content-type': 'application/json', }, withCredentials: true, }) .then((res) => { if (mounted) { const newAuthState = res.data.isAuthenticated; setIsAuthenticated(newAuthState); // 更新本地缓存 localStorage.setItem('isAuthenticated', newAuthState); } }) .catch(() => { if (mounted) { setIsAuthenticated(false); localStorage.setItem('isAuthenticated', 'false'); } }); return () => mounted = false; },[setIsAuthenticated]) return isAuthenticated; }
这种方案首次渲染就能返回非null的初始值,只有当服务端校验结果和本地缓存不一致时才会触发二次渲染,适合对首屏加载速度要求高的场景。
方案3:使用React 18+ Suspense能力
如果你使用的是React 18及以上版本,可以把认证请求包装成符合Suspense规范的Promise,上层组件用<Suspense>包裹处理加载态,Hook可以直接返回最终的认证结果,该方案适合已经在项目中使用Suspense处理异步数据请求的场景。
内容的提问来源于stack exchange,提问作者Usama
相关产品推荐
相关产品推荐

