TanStackT
TanStack8mo ago
1 reply
dual-salmon

Not using index as key while looping values lead to `field.state.value is undefined` when deleting

Here is my minimal example of one of my complex forms in a project:
const formOpts = formOptions({
  defaultValues: { values: [] as { id: number; value: string }[] },
});

function RouteComponent() {
  const form = useAppForm({
    ...formOpts,
  });

  return (
    <form.AppForm>
      <button
        onClick={() =>
          form.pushFieldValue("values", {
            id: Date.now(),
            value: `Value - ${form.state.values.values.length}`,
          })
        }
      >
        Add
      </button>

      <Child form={form} />
    </form.AppForm>
  );
}

const Child = withForm({
  ...formOpts,
  render: ({ form }) => {
    const values = useStore(form.store, (store) => store.values.values);

    return (
      <>
        {values.map(({ id }, index) => (
          <li key={id}> 
            <GrandChild form={form} index={index} />
            <button onClick={() => form.removeFieldValue("values", index)}>
              Delete
            </button>
          </li>
        ))}
      </>
    );
  },
});

const GrandChild = withForm({
  ...formOpts,
  props: {} as { index: number },
  render: ({ form, index }) => (
    <form.AppField
      name={`values[${index}]`}
      children={(field) => <>{field.state.value.value}</>}
    />
  ),
});


Here, when I delete any items except the last one using the delete button of Child component, then it causes leads to field.state.value is undefined error.

If I change the key of li in Child component to index, then it works fine. I don't want to use index as keys for obvious reasons. Is there any way to fix this issue? Is there any other ways to do the same thing?

If you are wondering why I am separating GrandChild component, then let's just say that my form is much complicated than this example. That's why I must separate the component.
Was this page helpful?