You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 11:58:22