检查输入字段是否已更改
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>
);
}
我正在使用 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>
);
}