如何使用动态输入管理formdata中的嵌套对象?

问题描述 投票:0回答:1

我正在使用 React 创建一个具有多个动态输入的表单。 这是我的

form.tsx
:

interface Property {
  name: string;
  values: string;
}

function CreateForm({ categories }: { categories: CategoryField[] }) {
  const [properties, setProperties] = useState<Property[]>([]);
  const initialState = {
    message: null,
    errors: {},
  };

  function addProperty() {
    setProperties((prev) => {
      return [...prev, { name: '', values: '' }];
    });
  }
  function handlePropertyNameChange(
    index: number,
    property: Property,
    newName: string
  ) {
    setProperties((prev) => {
      const properties = [...prev];
      properties[index].name = newName;
      return properties;
    });
  }
  function handlePropertyValuesChange(
    index: number,
    property: Property,
    newValues: string
  ) {
    setProperties((prev) => {
      const properties = [...prev];
      properties[index].values = newValues;
      return properties;
    });
  }
  function removeProperty(indexToRemove: number) {
    setProperties((prev) => {
      return [...prev].filter((p, pIndex) => {
        return pIndex !== indexToRemove;
      });
    });
  }
  const [state, dispatch] = useFormState(saveCategory, initialState);

  console.log('properties: ', properties);
  return (
    <div>
      <form
        action={(form) => {
          const p = form.get('name');
          // const parent = form.get('')
          const k = new FormData();
          const k2 = new Array().concat(properties);

          console.log('k2 before split: ', k2, ' properties: ', properties);
          
          k2.forEach((p: any) => {
            p.values = p.values.split(',');
          });
          console.log('k2 after forEach: ', k2);
          k.append('properties', JSON.stringify(k2));
          if (p) {
            k.append('name', p);
          }

          dispatch(k);
        }}
      >
    /* more static inputs here */

     {/* properties */}
          <div className='mb-2'>
            <label className='block' htmlFor='properties'>
              Properties
            </label>
            <button
              onClick={addProperty}
              type='button'
              className='btn-default text-sm mb-2'
            >
              Add new property
            </button>
            {properties.length > 0 &&
              properties.map((property, index) => (
                <div key={index} className='flex gap-1 mb-2'>
                  <input
                    type='text'
                    className='mb-0'
                    onChange={(ev) =>
                      handlePropertyNameChange(index, property, ev.target.value)
                    }
                    value={property.name}
                    placeholder='property name (example: color)'
                  />
                  <input
                    type='text'
                    className='mb-0'
                    onChange={(ev) =>
                      handlePropertyValuesChange(
                        index,
                        property,
                        ev.target.value
                      )
                    }
                    value={property.values}
                    placeholder='values, comma separated'
                  />
                  <button
                    onClick={() => removeProperty(index)}
                    type='button'
                    className='btn-red'
                  >
                    Remove
                  </button>
                </div>
              ))}
          </div>
        </div>
        <Button type='submit'> Add category </Button>
      </form>
    </div>

这是我的

action.ts

const FormSchema = z.object({
  id: z.string(),
  name: z.string(),
  parentCategory: z
    .string({
      invalid_type_error: 'Please select a category',
    })
    .nullable()
    .optional(),
  properties: z.string(),
});


export type CategoryState = {
  errors?: {
    name?: string[];
    parentCategory?: string[];
    properties?: string[];
  };
  message?: string | null;
};

const CreateCategory = FormSchema.omit({ id: true });

export async function saveCategory(
  prevState: CategoryState,
  formData: FormData
) {
  console.log('FormData:::: ', formData, prevState);
  const validatedFields = CreateCategory.safeParse({
    parentCategory: formData.get('parentCategory'),
    properties: formData.get('properties'),
    name: formData.get('name'),
  });

  if (!validatedFields.success) {
    return {
      errors: validatedFields.error.flatten().fieldErrors,
      message: 'Missing Fields. Failed to Create Invoice.',
    };
  }

  // Prepare data for insertion into the database
  const { parentCategory, properties, name } = validatedFields.data;

  console.log(
    'This is the parsed data:::::  ',
    parentCategory,
    properties,
    name
  );

  const parsed = JSON.parse(properties);
  console.log('parsed with json: ', JSON.parse(properties));
..........

因此,整个想法是根据用户的需要添加任意数量的

properties
输入,其中每个属性都是一个字符串数组,以
,
分隔。 我想解析我的表单数据以实现这种格式:

{
name: 'whatevername'
properties: {
 property1name: ["array" , "of", "string values"],
 property2name: ["another property"]
}

}

现在我正在使用 useState 挂钩来管理它,以计算并记住有多少

properties
输入,然后将它们传递给调度操作。然而,尽管目前可行,但感觉不太对劲。真的感觉不太“优雅”

javascript reactjs typescript form-data
1个回答
0
投票

对于 React 中的表单,您可以使用

react-hook-form
库。它非常简单易学并且非常有用。

这是文档。它简单易学,并且比经典的受控形式具有一些优势。我强烈建议您将它用于表单。

© www.soinside.com 2019 - 2024. All rights reserved.