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

React TypeScript表单首次提交返回空数组问题求解

React+TypeScript表单提交后formData打印为空的问题解决

问题描述

我用React+TypeScript开发简易表单,已为各输入项绑定onChange事件维护子组件内的独立状态,提交时将这些状态合并后存入父组件的formData数组,但首次提交后打印formData总是为空数组。不想用async,求其他解决方法。

相关代码

MainPage组件

import React from "react";
import { useState } from "react";
import { Form } from "../components/Form/Form";
import { Navbar } from "../components/Navbar";


export const MainPage = () => {

  const [formData, setFormData] = useState([]);

  return (
    <div className="w-full flex">
      <Navbar />
      <Form formData={formData} setFormData={setFormData} />
    </div>
  );
};

Form组件

import React from "react";
import { useState } from "react";

interface Props{
  formData: string[];
  setFormData: React.Dispatch<React.SetStateAction<any>>;
}


export const Form: React.FC<Props> = ({setFormData, formData}) => {
  const [organization, setOrganization] = useState<string>("");
  const [title, setTitle] = useState<string>("");
  const [firstName, setFirstName] = useState<string>("");
  const [lastName, setLastName] = useState<string>("");
  const [languages, setLanguages] = useState<string>("");
  const [employmentType, setEmploymentType] = useState<string>("");
  const [profession, setProfession] = useState<string>("");
  const [proficiency, setProficiency] = useState<string>("");

  const submitData = (e: any) => {
    const data = {
      organization,
      title,
      firstName,
      lastName,
      languages,
      employmentType,
      profession,
      proficiency,
    };
    setFormData((prevData: any) => [...prevData, data]);
    console.log(formData)
  }


  return (
    <div className="w-full">
      <div className="w-3/4 mx-auto mt-5">
        <h1 className="text-2xl">About you</h1>
        <div className="top flex mt-16 gap-8">
          <div>
            <h2 className="text-xl font-bold">Personal info</h2>
            <p className="text-sm font-extralight">
              Provide your personal info
            </p>
          </div>
          <div className="flex flex-col gap-5">
            <div>
              <input
                onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                  setOrganization(e.target.value);
                }}
                className="w-full bg-gray-200 p-2 rounded-md placeholder:text-sm placeholder:text-black"
                placeholder="Organization"
              ></input>
            </div>
            <div className="flex gap-3">
              <select
                onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
                  setTitle(e.target.value);
                }}
                className="bg-gray-200 p-2 rounded-md placeholder:text-sm placeholder:text-black"
              >
                <option value="Title">Title</option>
                <option value="Mr">Mr</option>
                <option value="Mrs">Mrs</option>
                <option value="Miss">Miss</option>
                <option value="Ms">Ms</option>
              </select>
              <input
                onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                  setFirstName(e.target.value);
                }}
                className="bg-gray-200 p-2 rounded-md placeholder:text-sm placeholder:text-black"
                placeholder="First name"
              ></input>
              <input
                onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                  setLastName(e.target.value);
                }}
                className="bg-gray-200 p-2 rounded-md placeholder:text-sm placeholder:text-black"
                placeholder="Last name"
              ></input>
            </div>
          </div>
        </div>
      </div>

      <hr className="mt-16"></hr>

      <div className="bottom w-3/4 mx-auto mt-16">
        <div>
          <h2 className="text-xl font-bold">Professional info</h2>
          <p className="text-sm font-extralight">
            Provide your professional info
          </p>
        </div>
        <div className="mt-14">
          <div className="flex flex-col gap-8 items-center">
            <input
              onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                setLanguages(e.target.value);
              }}
              className="w-3/4 bg-gray-200 p-2 rounded-md placeholder:text-sm placeholder:text-black"
              placeholder="Language(s) separate with comma"
            ></input>
            <input
              onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                setEmploymentType(e.target.value);
              }}
              className="w-3/4 bg-gray-200 p-2 rounded-md placeholder:text-sm placeholder:text-black"
              placeholder="Type of employment"
            ></input>
            <input
              onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                setProfession(e.target.value);
              }}
              className="w-3/4 bg-gray-200 p-2 rounded-md placeholder:text-sm placeholder:text-black"
              placeholder="Profession"
            ></input>
            <input
              onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                setProficiency(e.target.value);
              }}
              className="w-3/4 bg-gray-200 p-2 rounded-md placeholder:text-sm placeholder:text-black"
              placeholder="Proficiency level"
            ></input>
          </div>
        </div>
      </div>
      <div className="w-3/4 flex justify-end mt-7">
        <button onClick={(e) => submitData(e)} className="bg-sky-800 p-2 text-white rounded-md">Submit</button>
      </div>
    </div>
  );
};

问题原因

React的setState是异步操作,调用setFormData后,当前函数作用域内的formData还是更新前的旧值,所以你马上打印会看到空数组,这是React状态更新的特性,和异步函数无关。

解决方法

1. 直接打印刚组装的提交数据

既然已经手动组装了data对象,直接打印它就能确认提交内容是否正确,不需要依赖还未更新的formData:

const submitData = (e: any) => {
  const data = {
    organization,
    title,
    firstName,
    lastName,
    languages,
    employmentType,
    profession,
    proficiency,
  };
  setFormData((prevData: any) => [...prevData, data]);
  console.log(data); // 直接打印组装好的最新数据
}

2. 用useEffect监听formData变化

如果需要在formData更新完成后执行操作(比如打印、发送请求),可以在父组件或子组件中用useEffect监听状态变化:

// 在MainPage组件中添加
import { useEffect } from "react";

export const MainPage = () => {
  const [formData, setFormData] = useState([]);

  useEffect(() => {
    if (formData.length > 0) {
      console.log(formData); // 状态更新完成后触发
    }
  }, [formData]);

  // ... 其他代码
};

3. 修正类型定义(重要)

你的Props定义里formData被声明为string[],但实际存入的是对象,这会导致类型不匹配,可能隐藏潜在问题。建议修正类型:

// 先定义表单数据的类型
interface FormItem {
  organization: string;
  title: string;
  firstName: string;
  lastName: string;
  languages: string;
  employmentType: string;
  profession: string;
  proficiency: string;
}

// 修正Props类型
interface Props{
  formData: FormItem[];
  setFormData: React.Dispatch<React.SetStateAction<FormItem[]>>;
}

// 同时修正父组件的初始状态类型
export const MainPage = () => {
  const [formData, setFormData] = useState<FormItem[]>([]);
  // ... 其他代码
};

4. 在setFormData的回调中处理最新状态

如果需要在状态更新的同时使用最新值,可以利用setFormData的函数式更新特性:

setFormData((prevData) => {
  const newData = [...prevData, data];
  console.log(newData); // 这里的newData就是更新后的最新数组
  return newData;
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 05:35:30