React路由兄弟组件状态持久化:Redux/Context选型咨询
用React Context实现分步表单状态管理
核心方案
在Layout组件中创建Context,维护一个记录各页面表单提交状态与数据的状态对象。First组件提交后更新该状态,Second、Third组件读取状态判断是否允许提交,同时可获取已提交的表单数据。这个方案无需Redux或react-persist,状态随Layout组件持久存在,不会因路由切换重置。
具体实现步骤
1. 搭建Context与Provider
在Layout中定义Context,用useState初始化状态,提供修改状态的方法,再用Provider包裹路由出口,让所有子页面能访问上下文。
// Layout.jsx import { createContext, useState, useContext } from 'react'; import { Outlet } from 'react-router-dom'; // 创建表单提交状态上下文 const FormSubmissionContext = createContext(); // 自定义Hook,简化子组件调用 export const useFormSubmission = () => { const context = useContext(FormSubmissionContext); if (!context) { throw new Error('useFormSubmission必须在FormSubmissionProvider内部使用'); } return context; }; export default function Layout() { // 初始化各页面提交状态,默认未提交 const [formSubmissions, setFormSubmissions] = useState({ first: { submitted: false, data: null }, second: { submitted: false, data: null }, third: { submitted: false, data: null }, }); // 更新指定页面的提交状态与数据 const updateSubmission = (page, data) => { setFormSubmissions(prev => ({ ...prev, [page]: { submitted: true, data } })); }; return ( <FormSubmissionContext.Provider value={{ formSubmissions, updateSubmission }}> {/* 路由出口,渲染First/Second/Third页面 */} <Outlet /> </FormSubmissionContext.Provider> ); }
2. First组件:提交表单并更新上下文
First组件提交时调用updateSubmission,标记自身为已提交并保存表单数据。
// First.jsx import { useState } from 'react'; import { useFormSubmission } from './Layout'; import { useNavigate } from 'react-router-dom'; export default function First() { const [formData, setFormData] = useState({ name: '', email: '' }); const { updateSubmission } = useFormSubmission(); const navigate = useNavigate(); const handleSubmit = (e) => { e.preventDefault(); // 表单验证逻辑 if (!formData.name || !formData.email) return; // 更新上下文状态 updateSubmission('first', formData); // 可选:提交后跳转到Second页面 navigate('/second'); }; return ( <form onSubmit={handleSubmit}> <div> <label>姓名:</label> <input type="text" value={formData.name} onChange={(e) => setFormData(prev => ({...prev, name: e.target.value}))} /> </div> <div> <label>邮箱:</label> <input type="email" value={formData.email} onChange={(e) => setFormData(prev => ({...prev, email: e.target.value}))} /> </div> <button type="submit">提交First表单</button> </form> ); }
3. Second组件:读取状态控制提交权限
Second组件读取First的提交状态,未提交则禁用提交按钮并提示,已提交则允许提交,还可读取First的表单数据。
// Second.jsx import { useState } from 'react'; import { useFormSubmission } from './Layout'; export default function Second() { const [formData, setFormData] = useState({ address: '', phone: '' }); const { formSubmissions, updateSubmission } = useFormSubmission(); const isFirstSubmitted = formSubmissions.first.submitted; const handleSubmit = (e) => { e.preventDefault(); if (!isFirstSubmitted) { alert('请先提交First页面的表单'); return; } // 表单验证逻辑 if (!formData.address || !formData.phone) return; // 更新上下文状态 updateSubmission('second', formData); alert('Second表单提交成功'); }; return ( <div> {isFirstSubmitted && ( <p>已读取First提交的数据:姓名{formSubmissions.first.data.name},邮箱{formSubmissions.first.data.email}</p> )} <form onSubmit={handleSubmit}> <div> <label>地址:</label> <input type="text" value={formData.address} onChange={(e) => setFormData(prev => ({...prev, address: e.target.value}))} /> </div> <div> <label>电话:</label> <input type="tel" value={formData.phone} onChange={(e) => setFormData(prev => ({...prev, phone: e.target.value}))} /> </div> <button type="submit" disabled={!isFirstSubmitted}> {isFirstSubmitted ? '提交Second表单' : '请先提交First表单'} </button> </form> </div> ); }
4. Third组件:复用Second的逻辑
Third组件逻辑和Second一致,通过读取First的提交状态控制提交权限:
// Third.jsx import { useState } from 'react'; import { useFormSubmission } from './Layout'; export default function Third() { const [formData, setFormData] = useState({ note: '' }); const { formSubmissions, updateSubmission } = useFormSubmission(); const isFirstSubmitted = formSubmissions.first.submitted; const handleSubmit = (e) => { e.preventDefault(); if (!isFirstSubmitted) { alert('请先提交First页面的表单'); return; } if (!formData.note) return; updateSubmission('third', formData); alert('所有表单提交完成'); }; return ( <div> <form onSubmit={handleSubmit}> <div> <label>备注:</label> <textarea value={formData.note} onChange={(e) => setFormData(prev => ({...prev, note: e.target.value}))} /> </div> <button type="submit" disabled={!isFirstSubmitted}> {isFirstSubmitted ? '提交Third表单' : '请先提交First表单'} </button> </form> </div> ); }
5. 路由配置
确保所有子路由被Layout包裹,这样子页面才能访问Context:
// App.jsx import { BrowserRouter, Routes, Route } from 'react-router-dom'; import Layout from './Layout'; import First from './First'; import Second from './Second'; import Third from './Third'; function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<Layout />}> <Route index element={<First />} /> <Route path="second" element={<Second />} /> <Route path="third" element={<Third />} /> </Route> </Routes> </BrowserRouter> ); } export default App;
可选优化:刷新页面保留状态
如果需要刷新页面后不丢失提交状态,只需在Layout中加入localStorage同步逻辑,无需react-persist:
// Layout.jsx 中修改初始化与更新逻辑 const initialState = JSON.parse(localStorage.getItem('formSubmissions')) || { first: { submitted: false, data: null }, second: { submitted: false, data: null }, third: { submitted: false, data: null }, }; const [formSubmissions, setFormSubmissions] = useState(initialState); const updateSubmission = (page, data) => { const newState = { ...formSubmissions, [page]: { submitted: true, data } }; setFormSubmissions(newState); localStorage.setItem('formSubmissions', JSON.stringify(newState)); };
内容的提问来源于stack exchange,提问作者Lucky
相关产品推荐
相关产品推荐

