reactjs React钩子形式-追加到数组字段更新值,但不更新字段

eyh26e7m  于 2023-03-12  发布在  React
关注(0)|答案(2)|浏览(138)

我做了一个小的codesandbox,它有一个可重复的bug here,我有一个表单组件,我在那里用初始值设置useForm钩子,表单有一个IncomeInfo和一个数组字段组件作为子组件:

const { pageFormValues, setPageFormValues, setActiveStep } = usePageProvider();
const initialValues = pageFormValues ?? createInitialValues(income);
const [action, setAction] = useState<ActionStatus>(ActionStatus.IDLE);

const useFormMethods = useForm({
        defaultValues: initialValues,
});

return (
    <FormProvider {...useFormMethods}>
        <form onSubmit={useFormMethods.handleSubmit(onSubmit)}>
           <IncomeInfo />
           <IncomeBaseTable initialValues={initialValues} />

IncomeBaseTable看起来像这样:

export const IncomeBaseTable = ({ initialValues }) => (
    <TableWrapper heading={["Period", "Employer", "Total", ""]}>
        <ArrayField
            name="incomeBase"
            children={({ index, handleRemove, handleAppend }) => (
                <TableRowWrapper
                    cells={[
                        <div className="flex gap-x-4">
                            <FormControlledDatePicker
                                key={`incomeBase[${index}].from`}
                                name={`incomeBase[${index}].from`}
                                label="From"
                                placeholder="DD.MM.ÅÅÅÅ"
                                defaultValue={initialValues.incomeBase[index].from}
                                hideLabel
                            />
                            <FormControlledDatePicker
                                key={`incomeBase[${index}].to`}
                                name={`incomeBase[${index}].to`}
                                label="To"
                                placeholder="DD.MM.ÅÅÅÅ"
                                defaultValue={initialValues.incomeBase[index].to}
                                hideLabel
                            />
                        </div>,
                        <FormControlledTextField
                            key={`incomeBase[${index}].employer`}
                            name={`incomeBase[${index}].employer`}
                            label="Employer"
                            hideLabel
                        />,
                        <FormControlledTextField
                            key={`incomeBase[${index}].total`}
                            name={`incomeBase[${index}].total`}
                            label="Total"
                            type="number"
                            hideLabel
                        />,
                        <Button
                            key={`delete-button-${index}`}
                            onClick={() => handleRemove(index)}
                            icon={<Delete aria-hidden />}
                            variant="tertiary"
                            size="xsmall"
                        />,
                    ]}
                />
            )}
        />
    </TableWrapper>
);

TableWrapper看起来像这样:

export const TableWrapper = ({ heading, children }: { heading: string[]; children: ReactNode }) => (
    <Table size="small">
        <Table.Header>
            <Table.Row>
                {heading.map((header) => (
                    <Table.HeaderCell scope="col" key={header}>
                        {header}
                    </Table.HeaderCell>
                ))}
            </Table.Row>
        </Table.Header>
        <Table.Body>{children}</Table.Body>
    </Table>
);

export const TableRowWrapper = ({ cells }: { cells: ReactElement[] }) => (
    <Table.Row>
        {cells.map((cell, index) => {
            if (!index)
                return (
                    <Table.HeaderCell scope="row" key={cell.key}>
                        {cell}
                    </Table.HeaderCell>
                );
            return <Table.DataCell key={index}>{cell}</Table.DataCell>;
        })}
    </Table.Row>
);

当我像这样从IncomeInfo追加到field数组时,值会被更新,但是field和追加操作之前是一样的:

const { control, getValues } = useFormContext();
const { append, remove } = useFieldArray({
    control,
    name: "incomeBase",
});

const handleOnChange = (checked, incomeValues) => {
    if (checked) {
        append({
            from: null,
            to: null,
            employer: "",
            total: incomeValues.sum,
        });
    }
};

当我登录ArrayFields组件时,我可以看到使用getValues方法获得了新值,但fields没有更新:

const ArrayFields = ({ name, children }: ArrayFieldsProps) => {
    const { control, getValues } = useFormContext();
    const { fields, append, remove } = useFieldArray({
        control,
        name: name,
    });

    const handleAppend = (value) => {
        append(value);
        console.log("add: ", getValues());
    };

    const handleRemove = (index) => {
        remove(index);
        console.log("remove: ", getValues());
    };

    console.log("field", fields);
    console.log("value", getValues().incomeBase);

由于字段未更新,因此新字段未呈现。为什么值更新,而字段未更新?
如果我从IncomeBaseTable组件追加数组字段,那么它会追加并重新呈现新字段:

export const IncomeBaseTable = ({ initialValues }) => (
    <TableWrapper heading={["Period", "Employer", "Total", ""]}>
        <ArrayField
            name="incomeBase"
            children={({ index, handleRemove, handleAppend }) => (
                <TableRowWrapper
                    cells={[
                        <div className="flex gap-x-4">
                            <FormControlledDatePicker
                                key={`incomeBase[${index}].from`}
                                name={`incomeBase[${index}].from`}
                                label="From"
                                placeholder="DD.MM.ÅÅÅÅ"
                                defaultValue={initialValues.incomeBase[index].from}
                                hideLabel
                            />
                            <FormControlledDatePicker
                                key={`incomeBase[${index}].to`}
                                name={`incomeBase[${index}].to`}
                                label="To"
                                placeholder="DD.MM.ÅÅÅÅ"
                                defaultValue={initialValues.incomeBase[index].to}
                                hideLabel
                            />
                        </div>,
                        <FormControlledTextField
                            key={`incomeBase[${index}].employer`}
                            name={`incomeBase[${index}].employer`}
                            label="Employer"
                            hideLabel
                        />,
                        <FormControlledTextField
                            key={`incomeBase[${index}].total`}
                            name={`incomeBase[${index}].total`}
                            label="Total"
                            type="number"
                            hideLabel
                        />,
                        <Button
                            key={`add-button-${index}`}
                            onClick={() => handleAppend(fieldInitialValues)}
                            icon={<Add aria-hidden />}
                            variant="tertiary"
                            size="xsmall"
                        />,
                    ]}
                />
            )}
        />
    </TableWrapper>
);

为什么当我从IncomeBaseTable追加时,它会添加到字段数组并重新呈现,而当我从IncomeInfo组件追加时,它不会?

更新

我在父组件中使用useArrayField的一个示例,然后将其作为一个 prop 传递给子组件,从而使其工作。当我有多个示例具有相同的字段名称时,它将不工作。不知道为什么,如果我需要将其作为一个 prop 传递,则会破坏作为一个钩子的目的。

taor4pac

taor4pac1#

react-hook-form在幕后使用proxies来减少渲染的数量。在您的情况下,您可以更新钩子useArrayField之外的字段。这个钩子有自己的方法来检测更改,并且不期望外部更改。
您可以将useArrayField值转发到<IncomeInfo/>或使用useWatch()获取字段值。

**EDIT:**文档最后一节有一个很好的示例(受控字段数组):https://react-hook-form.com/api/usefieldarray/

eufgjt7s

eufgjt7s2#

我建议从useFormContext本身获取fieldsupdated fields,以便您可以访问form contextupdated fields
下面是IncomeInfo,您将在其中追加字段

import { useFieldArray, useFormContext } from "react-hook-form";
import { v1 as uuid} from "uuid";
export const IncomeInfo = ({ fieldArrayMethods }) => {
  const { getValues,control } = useFormContext();
  const { append, remove } = useFieldArray({
    control,
    name: "incomeBase"
  });

  const handleOnClick = () => {
    append({
      from: "",
      to: "",
      employer: "",
      total: "",
      id: uuid()
    });
  };
  console.log("append: ", getValues());

  return (
    <button type="button" onClick={() => handleOnClick()}>
      add from outside
    </button>
  );
};

IncomeBaseTable中获取fields,需要借助getValues()重载方法,特别是getValues(name?: name)

import { TableWrapper, TableRowWrapper } from "./TableWrapper";
import { Button } from "@navikt/ds-react";
import ArrayFields from "./ArrayField";
import { FormControlledTextField } from "./FormControlledTextField";
import { useFormContext } from "react-hook-form";

export const IncomeBaseTable = ({ initialValues, fieldArrayMethods }) => {
  const {getValues} = useFormContext();
  const incomeBaseFields = getValues("incomeBase");

  console.log("updated/initial fields",incomeBaseFields)
  return(
    <TableWrapper heading={["From", "To", "Employer", "Total", ""]}>
      <ArrayFields
        name="incomeBase"
        fields={incomeBaseFields}
        children={({ index }) => (
          <TableRowWrapper
            cells={[
              <FormControlledTextField
                key={`incomeBase[${index}].from`}
                name={`incomeBase[${index}].from`}
              />,
              <FormControlledTextField
                key={`incomeBase[${index}].to`}
                name={`incomeBase[${index}].to`}
              />,
              <FormControlledTextField
                key={`incomeBase[${index}].employer`}
                name={`incomeBase[${index}].employer`}
              />,
              <FormControlledTextField
                key={`incomeBase[${index}].total`}
                name={`incomeBase[${index}].total`}
              />,
              <Button
                key={`add-button-${index}`}
                type="button"
                onClick={() =>
                  fieldArrayMethods.append({
                    from: null,
                    to: null,
                    employer: "",
                    total: 123213
                  })
                }
                variant="tertiary"
                size="xsmall"
              >
                Add from inside
              </Button>
            ]}
          />
        )}
      />
    </TableWrapper>
  );
}

Check the fix here

相关问题