检查输入字段是否已更改

4

我正在使用React制作一个步进表单。而且表单结构几乎完成了。

有两个步骤,

-> Basic Details

-> Employment Details

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

form-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);

需求:

-> 点击 下一页/上一页 按钮时,需要检查是否有任何输入内容被更改。

-> 通过这种方式我可以调用API,在单击下一个按钮时保存更改并在实际应用程序中使用。

-> 如果您能帮忙打印一些 控制台日志 那就太好了。

例如:

-> 如果用户将基本详细信息部分的名字从John更改为Doe,则单击下一页按钮时可以在控制台日志中记录基本详细信息已更改。

-> 如果基本详细信息部分没有更改,则可以直接进入下一步(与现在类似)。

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

Edit next-dynamic-testing-issue (forked)

1个回答

2
我认为这里的主要问题是,你当前的实现会在每次更改时替换现有状态,因此无法知道是否/有什么更改。即使使用“usePrevious”钩子来保存先前的状态值,在这里也不起作用,因为它只会保存上一个字段编辑,而不是原始值,以进行比较。许多表单处理解决方案通过保留未被修改的初始状态或跟踪“脏”字段等来处理此问题。
以下是我的建议,可以调整表单上下文状态以跟踪已更新的字段。 “dirtyFields”对象保存原始值。
FormProvider
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>
    </>
  );
};

在增加/减少步骤时,请检查脏字段。

为每个部分赋予与表单上下文中的“表单键”匹配的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);
};

由于表单上下文中存在额外的嵌套状态,因此这里提供了一种实用工具,可将其缩减回仅在审核步骤中要呈现的表单数据。请保留HTML标签。
const prettyReview = sections.reduce(
  (sections, section) => ({
    ...sections,
    ...(value[section.id]
      ? { [section.id]: { ...value[section.id].fields } }
      : {}),
  }),
  {},
);

...

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

Edit check-if-input-field-is-changed

编辑

你说你的数据来自于后端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>
  );
}

@Undefined,我以为我没有,但我把它保存在我的账户中,所以我一定打算这样做,只是忘记在我的答案中添加链接了。我现在已经添加了链接。 - Drew Reese
@Undefined 哦,我明白了...你可以从后端数据对象创建一个新对象,基本上是将其浅复制到新的形状并添加“dirty”属性。假设你的后端数据是你问题中的当前形状,如果需要的话,我可以在明天早上更新我的答案,提供一个映射函数。 - Drew Reese
@Undefined 更新了答案,加入了一个初始化函数来映射数据形状。这应该可以让你接近你所寻求的内容。 - Drew Reese
兄弟,你能帮我解决一个问题吗?我从你之前的答案中找到了解决方案,但它不起作用... 问题在这里:https://dev59.com/RL7pa4cB1Zd3GeqPvksA - Undefined
你有机会帮我解决上面提出的问题吗?昨天起就在等待你的解决方案了。请帮帮我,Reese。 - Undefined
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接