使用React Hooks+useHistory跳转页面后获取Context为undefined如何解决
问题根因
- Context作用域不匹配:你定义的
classSelectedContext.Provider仅包裹了ClassSelection组件内部引入的ClassDetails实例,而路由配置中/class-detail路径对应的ClassDetails是独立渲染的,不在Provider的作用域范围内,自然无法拿到Context传递的值。 - 状态更新异步问题:
setSelectedClass是异步更新的,你调用该方法后立即执行跳转,此时selectedClass还未更新为最新的选中值,即使作用域正确也可能拿到旧值。 - useEffect依赖错误:
ClassSelection中拉取课程列表的useEffect依赖了selectedClass,会导致每次切换选中课程都重复拉取课程列表,属于不必要的请求。ClassDetails中的useEffect使用了空依赖数组,依赖的Context/路由参数更新时不会重新触发请求。
推荐解决方案(路由传参,实现最简单符合场景需求)
步骤1:修改路由配置,新增动态参数
// 路由文件中修改class-detail的路由规则 <PrivateRoute path="/class-detail/:classId" exact> <ClassDetails /> </PrivateRoute>
步骤2:修改ClassSelection的跳转逻辑
删除无用的selectedClass状态、Context定义、组件内部引入的ClassDetails和Provider包裹代码,直接将课程ID携带在路由中跳转:
// 修改跳转方法,接收课程ID参数 const onSelectedClassClicked = (classId) =>{ history.push(`/class-detail/${classId}`) } // 修改按钮点击事件 <button value={item.classIdentifier} onClick={(e) => { onSelectedClassClicked(e.target.value) }}> {item.className} </button> // 调整拉取课程列表的useEffect依赖为空数组,仅初始化时拉取一次 useEffect( () =>{ // 原有axios请求逻辑不变 },[]);
步骤3:修改ClassDetails,从路由取参数
import { useHistory, useParams } from 'react-router-dom'; // 新增引入useParams export const ClassDetails = () => { const [token, setToken] = useToken(); const history = useHistory(); const { classId } = useParams(); // 从路由参数获取选中的课程ID const authStr = 'Bearer '.concat(token); useEffect( () =>{ if(!classId) return; // 无参数时跳过请求 console.log(classId) axios.get(`http://localhost:8081/user/class/${classId}`, { headers: { 'Authorization': authStr } }) .then (res => { console.log(res.data); }) .catch( err =>{ console.log(err); console.log("An error happenned") } ); }, [classId, authStr]); // 依赖加上classId,参数变化时重新请求 // 其余渲染逻辑不变 }
若坚持使用Context实现的解决方案
- 将
classSelectedContext.Provider提升到路由组件的外层,包裹所有路由组件,同时将selectedClass状态和更新方法也提升到该层级,确保所有路由下的组件都能访问到Context。 - 调整跳转逻辑:由于
setSelectedClass是异步的,不要在调用后立即跳转,可以新增useEffect监听selectedClass变化,当值不为空时再执行跳转。
内容的提问来源于stack exchange,提问作者msd026
相关产品推荐
相关产品推荐

