React路由切换时维护子组件上下文数据的问题求助(新手实战)
你遇到的核心问题是:按钮的状态存在组件本地的useState中,且Context里只维护了单个按钮的状态。当路由切换时,Component1及其内部的Card、Button组件会被卸载,本地state完全丢失;切回原路由时,Button组件重新初始化,只能从Context里拿到最后一次更新的单个按钮状态,其他按钮的loading状态就丢失了。
另外,当前requestStartDbObj是单个对象,无法同时存储多个按钮的状态,这也是多个按钮同时操作时状态无法正确维护的隐患。
我们需要把所有按钮的状态统一维护在Context中,并且Button组件直接从Context读取对应id的状态,不再依赖本地state。这样无论组件卸载再挂载,都能从Context获取最新的状态。
步骤1:重构Context的状态结构
将Context中的状态改成一个键值对对象,key是按钮的id,value是对应的状态(start/loading/stop),默认状态可以初始化所有按钮的初始值。
假设你的DbContext的Provider是这样的,修改它的状态:
// Context/sharedContext.js import { createContext, useState } from 'react'; const DbContext = createContext(); export const DbProvider = ({ children }) => { // 初始化所有按钮的状态,对应Component1里的data const [buttonStates, setButtonStates] = useState({ 1: "start", 2: "start", 3: "start" }); return ( <DbContext.Provider value={{ buttonStates, setButtonStates }}> {children} </DbContext.Provider> ); }; export default DbContext;
步骤2:修改Button组件,移除本地state
让Button直接从Context中获取对应id的状态,点击时更新Context里的状态,模拟API请求完成后再更新状态:
// Button.jsx import React, { useContext } from 'react' import DbContext from '../Context/sharedContext' function Button(props) { const { buttonStates, setButtonStates } = useContext(DbContext); const buttonId = props.status.id; const currentState = buttonStates[buttonId]; const handleStart = () => { // 先设置为loading状态 setButtonStates(prev => ({ ...prev, [buttonId]: "loading" })); // 模拟API请求,这里用setTimeout代替 setTimeout(() => { // 请求完成后设置为stop状态 setButtonStates(prev => ({ ...prev, [buttonId]: "stop" })); }, 5000); } return ( <div> <button onClick={handleStart}>{currentState}</button> </div> ) } export default Button
步骤3:更新Card组件的传参
Card组件不需要再传递requestStartDbObj,因为Button直接从Context拿状态:
// Card.jsx import React from 'react'; import Button from './Button'; function Card(props) { return ( <div> <h1>{props.data.name}</h1> <Button status={props.data} /> </div> ) } export default Card;
步骤4:(可选)动态初始化Context状态
如果你的按钮数据是动态获取的,而不是硬编码的,可以在Component1中把数据传递给Context,或者在Provider中通过useEffect动态初始化。比如:
// 在DbProvider中 export const DbProvider = ({ children, initialButtonData }) => { const [buttonStates, setButtonStates] = useState(() => { // 从传入的初始数据生成状态对象 return initialButtonData.reduce((acc, item) => { acc[item.id] = item.status; return acc; }, {}); }); // ... 其他逻辑 } // 在Component1中 function Component1() { let data = [ { id: 1, name: "card1", status: "start", }, { id: 2, name: "card2", status: "start", }, { id: 3, name: "card3", status: "start", }, ]; return ( <div> <h1>Hello</h1> {data.map((d, i) => ( <Card key={i} data={d} /> ))} </div> ); }
- 状态集中管理:所有按钮的状态都存在Context中,组件卸载再挂载时,能直接从Context读取最新状态,不会丢失pending的loading状态。
- 支持多按钮同时操作:用键值对存储每个按钮的状态,多个按钮的状态更新互不干扰。
- 移除本地状态依赖:Button组件不再维护自己的state,完全依赖Context,避免了组件卸载时状态丢失的问题。
这样修改后,无论你怎么切换路由,只要API请求还在pending(也就是setTimeout还没执行完),按钮的loading状态都会被Context保留,切回路由后会正确显示loading,直到请求完成后变成stop状态。
内容的提问来源于stack exchange,提问作者tyler

