React表单输入数据存入Context的useState对象数组无更新问题排查
问题原因
- Provider位置错误:你在
AddRideForm2组件内部包裹了RidesContextProvider,但useContext(RidesContext)的调用在Provider渲染之前执行,拿到的上下文并非当前组件内部Provider的实例,修改的状态也和这个内部Provider无关。Provider应当放在所有需要使用该上下文的组件的共同父级(比如App根组件),不能套在使用上下文的组件内部。 - 添加逻辑错误:
rides本身是数组类型,你在addRideHandler中直接执行setRides(data),会把整个数组直接替换为新提交的单个行程对象,覆盖原有所有数据。 - 打印时机错误:React的状态更新是异步批量执行的,调用
ctx.onAdd后立刻打印ctx.ridesList,拿到的永远是更新前的旧值,属于正常现象。
修复代码
1. 修复rides-context.js
const RidesContext = React.createContext({ ridesList: [], // 修正默认值为数组,和实际类型对齐 onAdd: (data) => {}, onDelete: () => {}, }); export const RidesContextProvider = (props) => { const [rides, setRides] = useState([ { hour: "12:00", name: "Jan", }, { hour: "12:00", name: "Bartek ", }, { hour: "12:00", name: "Kamil", }, ]); const addRideHandler = (data) => { // 新数据追加到原有数组末尾,不覆盖历史数据 setRides(prevRides => [...prevRides, data]); }; return ( <RidesContext.Provider value={{ ridesList: rides, onAdd: addRideHandler, }} > {props.children} </RidesContext.Provider> ); }; export default RidesContext;
2. 修复AddRideForm.js
const AddRideForm2 = () => { const [userInput, setUserInput] = useState({ name: "", hour: "", }); const ctx = useContext(RidesContext); // 监听ridesList变化,打印更新后的最新值 useEffect(() => { console.log('更新后的行程列表:', ctx.ridesList); }, [ctx.ridesList]) const nameHandler = (e) => { setUserInput((prevState) => { return { ...prevState, name: e.target.value }; }); }; const hourHandler = (e) => { setUserInput((prevState) => { return { ...prevState, hour: e.target.value }; }); }; const submitHandler = (e) => { e.preventDefault(); const rideData = { name: userInput.name, hour: userInput.hour, }; ctx.onAdd(rideData); console.log('提交的表单数据:', userInput); }; // 移除组件内部的RidesContextProvider return ( <form className={classes.addRideFormContainer} onSubmit={submitHandler}> <Input className={classes.addRideFormInput} value={userInput.name} onChange={nameHandler} ></Input> <Input className={classes.addRideFormInput} value={userInput.hour} onChange={hourHandler} ></Input> <button className={classes.addRideFormButton}>Dodaj</button> </form> ); }; export default AddRideForm2;
3. 父组件引入Provider(示例)
需要在所有使用RidesContext的组件的共同父级包裹Provider,比如App.js:
import { RidesContextProvider } from './rides-context' import AddRideForm2 from './AddRideForm' function App() { return ( <RidesContextProvider> <AddRideForm2 /> {/* 其他需要使用行程上下文的组件都放在此处 */} </RidesContextProvider> ) }
内容的提问来源于stack exchange,提问作者Sosek
相关产品推荐
相关产品推荐

