React结合Context API时如何通过异步函数设置useState初始值
解决方案
方案1:全局加载兜底(最稳妥)
你不可能在React组件渲染前完成异步接口请求,useEffect本身的执行时机就晚于首次渲染,所以正确的做法是在登录状态未返回前,不渲染应用业务内容,避免子组件拿到undefined的loggedIn值。
修改后的Provider代码如下:
function AuthContextProvider(props) { const [loggedIn, setLoggedIn] = useState(undefined); async function getLoggedIn() { const loggedInRes = await Axios.get("http://localhost:5000/api/users/loggedIn"); setLoggedIn(loggedInRes.data); } useEffect(() => { getLoggedIn(); }, []); // 登录状态未获取到之前渲染加载UI if (loggedIn === undefined) { return <div>加载中...</div> // 可替换为全局骨架屏、加载动画组件 } return ( <AuthContext.Provider value={{ loggedIn, getLoggedIn }}> {props.children} </AuthContext.Provider> ) }
这样所有消费AuthContext的子组件,拿到的loggedIn只会是true或false,不会出现undefined。
方案2:本地存储作为初始值(优化用户体验)
如果不想每次打开应用都出现全局加载,可以在用户登录成功时将登录状态存入localStorage,初始值从本地存储读取,再用接口校验状态有效性,避免本地存储被篡改:
function AuthContextProvider(props) { // 初始值从localStorage取,没有的话默认是false const [loggedIn, setLoggedIn] = useState(() => { return localStorage.getItem('isLoggedIn') === 'true' }); async function getLoggedIn() { try { const loggedInRes = await Axios.get("http://localhost:5000/api/users/loggedIn"); setLoggedIn(loggedInRes.data); // 更新本地存储的状态 localStorage.setItem('isLoggedIn', loggedInRes.data) } catch (e) { // 接口校验失败,重置登录状态 setLoggedIn(false); localStorage.setItem('isLoggedIn', 'false') } } useEffect(() => { getLoggedIn(); }, []); return ( <AuthContext.Provider value={{ loggedIn, getLoggedIn }}> {props.children} </AuthContext.Provider> ) }
这个方案可以让应用秒开,只有当接口校验出本地状态无效时,再做跳登录等逻辑处理。
方案3:消费层单独处理(不需要全局加载时使用)
如果不想做全局拦截,可以在所有消费AuthContext的地方做状态判断,比如路由守卫组件:
function PrivateRoute({ children }) { const { loggedIn } = useContext(AuthContext); const navigate = useNavigate(); if (loggedIn === undefined) { return <div>页面加载中...</div> } return loggedIn ? children : navigate('/login'); }
然后把需要登录才能访问的路由都用PrivateRoute包裹即可。
内容的提问来源于stack exchange,提问作者Hanis Hapsa
相关产品推荐
相关产品推荐

