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

为多输入项的useState设置唯一Key问题求解

问题解决:动态新增输入框同步变化的问题

你的推测有部分道理,但核心问题有两个:组件key不唯一,以及训练次数的输入框没有设置独立的name属性,导致所有组共用同一个Formik状态字段,修改一个就会同步所有。下面是具体的修复步骤:

1. 修复组件Key问题

你当前用key={n},但[...Array(numberOfMusclesInProgram)]生成的数组元素全是undefined,所以所有组件的key都是重复的,React无法区分不同的组件实例。改成用map的索引i作为key:

{[...Array(numberOfMusclesInProgram)].map((n, i) => (
  <DisplayMuscleInProgram key={i} index={i} />
))}

同时给DisplayMuscleInProgram组件添加index属性,用来区分不同的组。

2. 给训练次数输入框设置独立的name

现在所有训练次数的Field用的都是name="numberOfExercices",这会导致Formik把所有输入框绑定到同一个状态字段。改成和肌肉类型一样,带上索引:

<Field
  as="select"
  className="col-span-2 rounded-lg bg-lightGray p-3"
  name={`numberOfExercices-${props.index}`}
>
  {numberOfExercices(10)}
</Field>

3. 调整初始值结构(可选但更规范)

原来的initialValues是单个字段,现在对应多组输入,应该初始化所有需要的字段;或者直接改用数组结构管理多组数据,后续维护更方便:

// 改用数组管理多组数据
const [muscleGroups, setMuscleGroups] = useState([{
  muscle: getAllMuscles()[0],
  numberOfExercices: 0
}]);

// 添加按钮逻辑改成
<button
  onClick={() => setMuscleGroups([...muscleGroups, {
    muscle: getAllMuscles()[0],
    numberOfExercices: 0
  }])}
  type="button"
  className="rounded-md border"
>
  Add
</button>

// 渲染时遍历数组
{muscleGroups.map((group, i) => (
  <DisplayMuscleInProgram key={i} index={i} />
))}

完整修改后的代码

import { Formik, Form, Field } from "formik";
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { getAllMuscles, numberOfExercices } from "../helpers/dataModuler";
import { setInLocalStorage } from "../helpers/localStorageHandler";

export const ProgramForm: React.FC<{}> = () => {
  // 用数组管理多组训练数据,结构更清晰
  const [muscleGroups, setMuscleGroups] = useState([{
    muscle: getAllMuscles()[0],
    numberOfExercices: 0
  }]);

  const navigate = useNavigate();

  const handleSubmit = (values: any) => {
    setInLocalStorage("program", values);
    navigate("/programme");
  };

  // 根据现有组数生成Formik初始值
  const getInitialValues = () => {
    const initialValues: any = {};
    muscleGroups.forEach((_, i) => {
      initialValues[`muscle-${i}`] = getAllMuscles()[0];
      initialValues[`numberOfExercices-${i}`] = 0;
    });
    return initialValues;
  };

  return (
    <>
      <Formik
        initialValues={getInitialValues()}
        onSubmit={(values) => handleSubmit(values)}
      >
        <Form>
          <div id="container">
            {muscleGroups.map((_, i) => (
              <DisplayMuscleInProgram key={i} index={i} />
            ))}
          </div>

          <button
            onClick={() => setMuscleGroups([...muscleGroups, {
              muscle: getAllMuscles()[0],
              numberOfExercices: 0
            }])}
            type="button"
            className="rounded-md border"
          >
            Add
          </button>

          <div className="text-center">
            <button
              type="submit"
              className="mt-8 rounded-lg bg-primary p-2 text-white"
            >
              Let's gooooo 💪
            </button>
          </div>
        </Form>
      </Formik>
    </>
  );
};

export const DisplayMuscleInProgram = (props: { index: number }) => {
  const listOfMuscles = getAllMuscles();

  return (
    <>
      <div className="my-4 grid grid-cols-5 justify-between gap-5">
        <Field
          as="select"
          className="col-span-3 rounded-lg bg-lightGray p-3"
          name={`muscle-${props.index}`}
        >
          {listOfMuscles.map((muscle, key) => (
            <option key={key} value={muscle}>{muscle}</option>
          ))}
        </Field>
        <Field
          as="select"
          className="col-span-2 rounded-lg bg-lightGray p-3"
          name={`numberOfExercices-${props.index}`}
        >
          {numberOfExercices(10)}
        </Field>
      </div>
    </>
  );
};

关键改动点总结

  • 替换了重复的key,用索引保证每个组件实例唯一
  • 给每个训练次数输入框设置了带索引的独立name,避免状态冲突
  • 改用数组管理多组数据,让状态结构更清晰,后续扩展更方便

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 07:15:52