useContext首次页面刷新无法读取值 触发JSON解析错误
React Context异步传值首屏刷新JSON解析异常
问题复现代码
App.jsx
export const LoginContext = React.createContext(); export const DetailsContext = React.createContext(); function App() { const username = localStorage.getItem("bankDetails"); const [userDetails, setUserDetails] = useState({}); const [isValid, setisValid] = useState(false); useEffect(() => { if (username !== null) { Axios.post("http://localhost:3001/userDetails", { username: username, }).then((res) => { if (res.data.err) { console.log("err"); } else { setUserDetails(res.data.details[0]); setisValid(true); } }); } }, []); return ( <LoginContext.Provider value={{ isValid, setisValid }}> <DetailsContext.Provider value={{ userDetails, setUserDetails }}> <Router> <Routes> <Route ... /> </Routes> </Router> </DetailsContext.Provider> </LoginContext.Provider> ); } export default App;
Transactions.js
function Transactions() { const { isValid } = useContext(LoginContext); const { userDetails, setUserDetails } = useContext(DetailsContext); const [allDetails, setAllDetails] = useState([]); const [transactions, setTransactions] = useState([]); useEffect(() => { console.log(userDetails); Axios.get("http://localhost:3001/transactTo").then((rest) => { setAllDetails(rest.data); }); // setTransactions(JSON.parse(userDetails.transactions)); }, [userDetails]); return isValid ? <h1>Valid</h1> : <h1>Not Valid</h1> } export default Transactions;
问题现象
- 页面首次硬刷新时,通过useContext获取的
userDetails首次控制台打印为空对象,等待组件重渲染后才能拿到正确数据对象 - 取消
setTransactions(JSON.parse(userDetails.transactions))行的注释后,首次渲染阶段直接抛出错误:Unexpected token u in JSON at position 0 - 异常仅在页面硬刷新时触发,通过路由导航从其他页面跳转进入时无该问题
- 尝试新增依赖项为
allDetails的useEffect执行交易数据解析逻辑,问题仍然存在,无效修复代码如下:
useEffect(() => { setTransactions(JSON.parse(userDetails.transactions)); }, [allDetails]);
根本原因
- 异步时序问题:App组件中
userDetails的初始状态是空对象{},用户详情的接口请求是异步执行的,组件首次挂载时Context下发的就是初始空对象,子组件首次渲染拿到的userDetails自然不存在transactions属性 - 报错触发逻辑:首次渲染时
userDetails.transactions值为undefined,JSON.parse接收到undefined参数时,会先将其转为字符串"undefined",解析第一个字符u时不符合JSON语法,直接抛出解析错误 - 路由跳转无异常的原因:前端路由跳转是单页应用内部导航,App根组件不会重新卸载挂载,跳转前接口请求已经完成、
userDetails已经被更新为正确值,子组件挂载时拿到的是加载完成的完整数据,不会触发空值解析 - 依赖
allDetails修复无效的原因:allDetails对应的接口请求和userDetails的接口请求是完全独立的两个异步任务,返回时机没有绑定关系,allDetails更新不代表userDetails已经加载完成、存在transactions字段,属于依赖项配置错误。
修复方案
- 给解析逻辑加非空守卫,等字段存在时再执行JSON解析,修改Transactions组件中对应useEffect:
useEffect(() => { console.log(userDetails); Axios.get("http://localhost:3001/transactTo").then((rest) => { setAllDetails(rest.data); }); // 先判断transactions字段存在再执行解析 if (userDetails?.transactions) { setTransactions(JSON.parse(userDetails.transactions)); } }, [userDetails]);
- 优化初始状态配置,增加加载态拦截,避免未加载完成时渲染业务逻辑:
// App.jsx 中将userDetails初始值改为null,区分未加载/已加载空数据的状态 const [userDetails, setUserDetails] = useState(null); // Transactions.jsx 渲染前增加加载判断 if (!isValid || !userDetails) { return <div>数据加载中...</div> } return <h1>Valid</h1>
- 修正useEffect依赖项:解析
transactions的逻辑依赖userDetails的变化,不要将无关联的allDetails设为依赖项,避免逻辑和依赖不匹配导致的执行时机错误。
内容的提问来源于stack exchange,提问作者Shubham Singhvi
相关产品推荐
相关产品推荐

