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

React+Inertia中提交数组形式表单的技术实现求助

React + Inertia + Laravel 学历表单数组提交最优实现方案

前端(React + Inertia)实现

1. 状态结构优化

放弃分开维护四个独立数组的方式,改用对象数组存储每一行学历信息,彻底避免索引不匹配的问题,代码更易维护:

// 用Inertia useForm简化状态管理(推荐)
import { useForm } from '@inertiajs/react';

const { data, setData, post, processing, errors } = useForm({
  qualifications: [
    { qualification: '', institution: '', course: '', date_of_graduation: '' }
  ]
});

2. 动态增删行方法

  • 添加行:直接在数组末尾追加空对象即可
const addQualification = () => {
  setData('qualifications', [
    ...data.qualifications,
    { qualification: '', institution: '', course: '', date_of_graduation: '' }
  ]);
};
  • 删除行:通过索引过滤对应行,保留至少一行避免空表单
const removeQualification = (index) => {
  if (data.qualifications.length <= 1) return;
  setData('qualifications', data.qualifications.filter((_, i) => i !== index));
};

3. 表单输入绑定

针对每行字段,通过setData的路径语法直接更新对应值:

const handleInputChange = (index, field, value) => {
  setData(`qualifications.${index}.${field}`, value);
};

4. 表单渲染

用map遍历数组渲染每行输入,同时回显验证错误:

{data.qualifications.map((item, index) => (
  <div key={index} className="flex gap-4 mb-4">
    <div>
      <input
        type="text"
        value={item.qualification}
        onChange={(e) => handleInputChange(index, 'qualification', e.target.value)}
        placeholder="学历"
      />
      {errors?.[`qualifications.${index}.qualification`] && (
        <span className="text-red-500">{errors[`qualifications.${index}.qualification`]}</span>
      )}
    </div>
    <div>
      <input
        type="text"
        value={item.institution}
        onChange={(e) => handleInputChange(index, 'institution', e.target.value)}
        placeholder="院校"
      />
      {errors?.[`qualifications.${index}.institution`] && (
        <span className="text-red-500">{errors[`qualifications.${index}.institution`]}</span>
      )}
    </div>
    <div>
      <input
        type="text"
        value={item.course}
        onChange={(e) => handleInputChange(index, 'course', e.target.value)}
        placeholder="课程"
      />
      {errors?.[`qualifications.${index}.course`] && (
        <span className="text-red-500">{errors[`qualifications.${index}.course`]}</span>
      )}
    </div>
    <div>
      <input
        type="date"
        value={item.date_of_graduation}
        onChange={(e) => handleInputChange(index, 'date_of_graduation', e.target.value)}
      />
      {errors?.[`qualifications.${index}.date_of_graduation`] && (
        <span className="text-red-500">{errors[`qualifications.${index}.date_of_graduation`]}</span>
      )}
    </div>
    <button onClick={() => removeQualification(index)} disabled={processing}>
      删除
    </button>
  </div>
))}
<button onClick={addQualification} disabled={processing}>添加一行</button>
<button onClick={() => post('/qualifications/store')} disabled={processing}>提交</button>

后端(Laravel)验证与处理

1. 路由定义

// routes/web.php
Route::post('/qualifications/store', [QualificationController::class, 'store'])->name('qualifications.store');

2. 控制器验证与存储

直接用Laravel的数组验证规则,支持嵌套字段校验:

// app/Http/Controllers/QualificationController.php
public function store(Request $request)
{
    $validated = $request->validate([
        'qualifications' => 'required|array|min:1',
        'qualifications.*.qualification' => 'required|string|max:255',
        'qualifications.*.institution' => 'required|string|max:255',
        'qualifications.*.course' => 'required|string|max:255',
        'qualifications.*.date_of_graduation' => 'required|date',
    ]);

    // 批量存储示例(根据你的业务逻辑调整)
    foreach ($validated['qualifications'] as $item) {
        auth()->user()->qualifications()->create($item);
    }

    return redirect()->back()->with('success', '学历信息提交成功');
}

方案优势

  • 解决了独立数组索引错乱导致的输入失效问题,对象数组结构更贴合业务逻辑
  • 借助Inertia的useForm自动处理CSRF、错误回显、加载状态,无需手动维护
  • Laravel原生支持嵌套数组验证,无需额外编写自定义规则

内容的提问来源于stack exchange,提问作者user17727484

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 12:55:11