React数据获取后消失:Context传递用户数据失效问题排查
问题描述
我通过Context与Provider实现用户详情的获取,将数据存入state变量与sessionStorage中,期望无需通过props即可为任意组件提供该数据。尝试实现一种按需获取userData的机制:当访问userData时自动触发API调用(必要时)。
控制台显示已成功获取到用户数据,但组件无法读取该数据。例如在ExampleComponent中访问userData.name时无内容,页面仅显示<h1>Username is </h1>。
相关代码如下:
UserDataProvider.jsx
import React, { useState, useContext, createContext } from "react"; import axios from 'axios' const UserContext = createContext({}) export const theUser = () => useContext(UserContext) const default_info = { name: null, age: null, hobbies: [], } export const UserDataProvider = ( { children } ) => { const [ userData, setUserData ] = useState(getUserData()) //gets user state from session storage OR a set of defaults OR from the server async function getUserData(){ var data = JSON.parse(sessionStorage.getItem("userData")) if (data == "undefined" || data == null){ console.log("no data in session - fetching from server") data = await FetchUserData() return data } console.log("Found data") console.table(data) return data } async function FetchUserData(){ var body_json = {"getinfo": 1} try{ await axios.post('/api/user_info', { headers: { 'Content-type':'application/json', 'Accept':'application/json', withCredentials: true }, body: JSON.stringify(body_json) }).then((response) => { if (response.statusText == "OK"){ var userDataObject = { name: response.data['name'], age: response.data['savings'], hobbies: response.data['hobbies'], } setUserData(userDataObject) sessionStorage.setItem('userData', JSON.stringify(userDataObject)) return userDataObject } else return null }); } catch(error){ console.error(error) return null } } const accessibleObjects = { userData } return ( <UserContext.Provider value={accessibleObjects}> { children } </UserContext.Provider> ) }
main.jsx
import { UserDataProvider } from './context/UserDataProvider.jsx'; ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <AuthProvider> <UserDataProvider> <Router > <App /> </Router> </UserDataProvider> </AuthProvider> </React.StrictMode>, )
问题分析与修正方案
你的代码存在几个关键问题,导致组件无法正确读取userData:
1. useState初始化使用异步函数的错误
useState(getUserData())中,getUserData是异步函数,返回值是Promise而非实际用户数据。这会导致初始state是Promise对象,组件读取时自然拿不到正确值。
修正方法:
改用useEffect处理异步初始化逻辑,初始state设为默认值:
const [userData, setUserData] = useState(default_info); const [isLoading, setIsLoading] = useState(true); useEffect(() => { async function initUserData() { const storedData = JSON.parse(sessionStorage.getItem("userData")); let finalData; if (storedData) { console.log("Found data in sessionStorage"); console.table(storedData); finalData = storedData; } else { console.log("no data in session - fetching from server"); finalData = await FetchUserData(); sessionStorage.setItem('userData', JSON.stringify(finalData)); } setUserData(finalData); setIsLoading(false); } initUserData(); }, []);
2. axios请求参数格式错误
axios.post的正确格式是axios.post(url, data, config),你错误地将body和headers放在了同一个对象里,且手动JSON.stringify请求体(axios会自动处理)。
修正方法:
async function FetchUserData() { const body_json = { getinfo: 1 }; try { const response = await axios.post('/api/user_info', body_json, { headers: { 'Content-type': 'application/json', 'Accept': 'application/json', }, withCredentials: true }); if (response.status === 200) { // 用status判断比statusText更可靠 return { name: response.data.name, age: response.data.savings, // 注意此处字段名是savings,确认是否为笔误 hobbies: response.data.hobbies, }; } else { return default_info; } } catch (error) { console.error(error); return default_info; } }
3. 按需获取逻辑的补充
若要实现“访问userData时自动触发请求”,可以通过getter属性包装userData,同时维护loading状态避免重复请求:
const accessibleObjects = { get userData() { // 若当前为默认值且未在加载中,触发请求 if (JSON.stringify(userData) === JSON.stringify(default_info) && !isLoading) { initUserData(); } return userData; }, isLoading };
注:更稳妥的方式是在组件中结合loading状态判断,避免getter中异步操作导致的状态异常。
4. 组件中正确使用Context
确保组件中正确调用theUser()获取上下文:
// ExampleComponent.jsx import { theUser } from './context/UserDataProvider'; function ExampleComponent() { const { userData, isLoading } = theUser(); return ( <h1>Username is {isLoading ? 'loading...' : userData.name}</h1> ); }
完整修正后的UserDataProvider.jsx
import React, { useState, useContext, createContext, useEffect } from "react"; import axios from 'axios' const UserContext = createContext({ userData: { name: null, age: null, hobbies: [] }, isLoading: false }) export const theUser = () => useContext(UserContext) const default_info = { name: null, age: null, hobbies: [], } export const UserDataProvider = ({ children }) => { const [userData, setUserData] = useState(default_info); const [isLoading, setIsLoading] = useState(true); async function FetchUserData() { const body_json = { getinfo: 1 }; try { const response = await axios.post('/api/user_info', body_json, { headers: { 'Content-type': 'application/json', 'Accept': 'application/json', }, withCredentials: true }); if (response.status === 200) { return { name: response.data.name, age: response.data.savings, hobbies: response.data.hobbies, }; } else { return default_info; } } catch (error) { console.error(error); return default_info; } } async function initUserData() { setIsLoading(true); const storedData = JSON.parse(sessionStorage.getItem("userData")); let finalData; if (storedData) { console.log("Found data in sessionStorage"); console.table(storedData); finalData = storedData; } else { console.log("no data in session - fetching from server"); finalData = await FetchUserData(); sessionStorage.setItem('userData', JSON.stringify(finalData)); } setUserData(finalData); setIsLoading(false); } useEffect(() => { initUserData(); }, []); const accessibleObjects = { get userData() { if (JSON.stringify(userData) === JSON.stringify(default_info) && !isLoading) { initUserData(); } return userData; }, isLoading }; return ( <UserContext.Provider value={accessibleObjects}> {children} </UserContext.Provider> ) }
内容的提问来源于stack exchange,提问作者scath
相关产品推荐
相关产品推荐

