检查输入字段是否已更改

Check if input field is changed

我正在使用 React 制作步进器表单。表单结构也差不多完成了..

有两步,

-> Basic Details

-> Employment Details

此处使用了表单上下文来填写输入字段的默认值,如果进行了任何更改,则会通过状态存储。

形式-context.js

import React, { useState } from 'react';

export const FormContext = React.createContext();

export function FormProvider({ children }) {
  const [formValue, setFormValue] = useState({
    basicDetails: {
      firstName: 'John',
      lastName: '',
    },
    companyDetails: {
      companyName: 'Some Company',
      designation: 'Some Designation',
    },
  });

  return (
    <FormContext.Provider value={[formValue, setFormValue]}>
      {children}
    </FormContext.Provider>
  );
}

这里有下一个和上一个按钮可以在步骤之间移动,

index.js:

  const next = () => setCurrentPage((prev) => prev + 1);
  const prev = () => setCurrentPage((prev) => prev - 1);

要求:

-> 点击 next/previous 按钮,我需要检查是否有任何输入被更改。

-> 通过这个我可以调用 API 来保存在我的真实应用程序中单击下一步按钮时的更改。

-> 在这里,如果你帮我做一些 console log 就足够了。

例如:

-> 如果用户将基本详细信息部分中的名字从 John 修改为 Doe 然后单击下一步按钮可以控制台记录基本详细信息发生变化。

-> 如果在基本详情部分更改了 none 个字段,则可以直接进行下一步(就像现在一样)。

注意:请不要硬编码任何输入名称,因为我的每个步骤都有 30 多个输入字段..

我认为这里的主要问题是您当前的实现替换了现有状态,每次更改,所以不可能知道if/what 改变了。甚至保存先前状态值的“usePrevious”挂钩在这里也不起作用,因为它只会保存最后一次先前的字段编辑,而不是原始值,以供比较。许多表单处理解决方案通过保持未改变的初始状态或跟踪“脏”字段来处理此问题,and/or 更多。

这是我的一个建议,可以调整您的表单上下文状态以跟踪已更新的字段。 dirtyFields 对象保留原始值。

表单提供者

const [formValue, setFormValue] = useState({
  basicDetails: {
    fields: {
      firstName: 'John',
      lastName: '',
    },
    dirtyFields: {},
  },
  companyDetails: {
    fields: {
      companyName: 'Some Company',
      designation: 'Some Designation',
    },
    dirtyFields: {},
  },
});

基本详情

const BasicDetails = () => {
  const [value, setValue] = React.useContext(FormContext);
  const {
    basicDetails: { fields }, // <-- get field values
  } = value;

  const handleInputChange = (event) => {
    const { name, value } = event.target;

    setValue((prev) => ({
      ...prev,
      basicDetails: {
        ...prev.basicDetails,
        fields: {
          ...prev.basicDetails.fields,
          [name]: value,
        },
        ...(prev.basicDetails.dirtyFields[name] // <-- only mark dirty once with original value
          ? {}
          : {
              dirtyFields: {
                ...prev.basicDetails.dirtyFields,
                [name]: prev.basicDetails.fields[name],
              },
            }),
      },
    }));
  };

  return (
    <>
      <div className="form-group col-sm-6">
        <label htmlFor="firstName">First Name</label>
        <input
          ...
          value={fields.firstName} // <-- access fields object
          ...
        />
      </div>
      <div className="form-group col-sm-4">
        <label htmlFor="lastName">Last Name</label>
        <input
          ...
          value={fields.lastName}
          ...
        />
      </div>
    </>
  );
};

就业详情

const EmploymentDetails = () => {
  const [value, setValue] = React.useContext(FormContext);
  const {
    companyDetails: { fields },
  } = value;

  const handleInputChange = (event) => {
    const { name, value } = event.target;

    setValue((prev) => ({
      ...prev,
      companyDetails: {
        ...prev.companyDetails,
        fields: {
          ...prev.companyDetails.fields,
          [name]: value,
        },
        ...(prev.companyDetails.dirtyFields[name]
          ? {}
          : {
              dirtyFields: {
                ...prev.companyDetails.dirtyFields,
                [name]: prev.companyDetails.fields[name],
              },
            }),
      },
    }));
  };

  return (
    <>
      <div className="form-group col-sm-6">
        <label htmlFor="companyName">Company Name</label>
        <input
          ...
          value={fields.companyName}
          ...
        />
      </div>
      <div className="form-group col-sm-4">
        <label htmlFor="designation">Designation</label>
        <input
          ...
          value={fields.designation}
          ...
        />
      </div>
    </>
  );
};

在 incrementing/decrementing 步骤时检查脏字段。

为每个部分指定一个与表单上下文中的“表单键”相匹配的 ID。

const sections = [
  {
    title: 'Basic Details',
    id: 'basicDetails',
    onClick: () => setCurrentPage(1),
  },
  {
    title: 'Employment Details',
    id: 'companyDetails',
    onClick: () => setCurrentPage(2),
  },
  { title: 'Review', id: 'review', onClick: () => setCurrentPage(3) },
];

创建一个 checkDirty 实用程序。这里我简单记录脏字段

const checkDirty = (page) => {
  console.log('check dirty', 'page', page);
  console.log(
    value[sections[page - 1].id] && value[sections[page - 1].id].dirtyFields,
  );
};

const next = () => {
  setCurrentPage((prev) => prev + 1);
  checkDirty(currentPage); // <-- check for dirty fields when updating page step
};

const prev = () => {
  setCurrentPage((prev) => prev - 1);
  checkDirty(currentPage);
};

由于表单上下文中的额外嵌套状态,这里有一个实用程序可以将其减少回 只是 您希望在审核步骤中呈现的表单数据。

const prettyReview = sections.reduce(
  (sections, section) => ({
    ...sections,
    ...(value[section.id]
      ? { [section.id]: { ...value[section.id].fields } }
      : {}),
  }),
  {},
);

...

<pre>{JSON.stringify(prettyReview, null, 2)}</pre>

编辑

您说您的数据来自后端 API 调用。这是一个上下文状态初始化函数,它将 API 数据形状映射到我拥有的状态形状。

给出的数据来自 API

const apiData = {
  basicDetails: {
    firstName: 'John',
    lastName: '',
  },
  companyDetails: {
    companyName: 'Some Company',
    designation: 'Some Designation',
  },
};

初始化函数

const initializeContext = (data) =>
  Object.entries(data).reduce(
    (sections, [section, fields]) => ({
      ...sections,
      [section]: {
        fields,
        dirtyFields: {},
      },
    }),
    {},
  );

初始化FormProvider上下文状态

function FormProvider({ children }) {
  const [formValue, setFormValue] = useState(initializeContext(apiData));

  return (
    <FormContext.Provider value={[formValue, setFormValue]}>
      {children}
    </FormContext.Provider>
  );
}