React 表单 useState 对象无法正常工作
React form useState object doesn't work as I want
我的 React 应用程序有问题。我有一个带有两个输入的表单,当我提交带有空输入的表单时,它应该在每个输入中呈现一条错误消息。问题是它不显示第一个输入。我怎样才能修复它以在每个错误中显示错误?实现在 useForm.js
.
FormUI图片:
我的代码:
Form.js
const Form = () => {
const formLogin = () => {
console.log("Callback function when form is submitted!");
console.log("Form Values ", values);
}
const {handleChange, values, errors, handleSubmit} = useForm(formLogin);
return (
<Wrapper>
<form onSubmit={handleSubmit}>
<div className="govgr-form-group gap-bottom">
<label className="govgr-label govgr-!-font-weight-bold" htmlFor="code">Code*</label>
{errors.code && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>{errors.code}</p>}
<input className={`govgr-input govgr-!-width-three-quarter ${errors.code ? 'govgr-error-input' : ''}`} id="code" name="code" type="text" onChange={handleChange} />
</div>
<fieldset>
<div className="govgr-form-group">
<label className="govgr-label govgr-!-font-weight-bold" htmlFor="first">Name*</label>
{errors.first && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>{errors.first}</p>}
<input className={`govgr-input govgr-!-width-three-quarter ${errors.first ? 'govgr-error-input' : ''}`} id="first" name="first" type="text" onChange={handleChange} />
</div>
</fieldset>
<button type="submit" className="govgr-btn govgr-btn-primary btn-center">Save</button>
</form>
</Wrapper>
);
};
export default Form;
useForm.js
:
const useForm = (callback) => {
const [values, setValues] = useState({});
const [errors, setErrors] = useState({});
const validate = (event, name, value) => {
event.persist();
switch (name) {
case "code":
if (value.trim() === "" || value.trim() === null) {
setErrors({
...errors,
code: "Code is required",
});
} else {
let newObj = omit(errors, "code");
setErrors(newObj);
}
break;
case "first":
if (value.trim() === "" || value.trim() === null) {
setErrors({
...errors,
first: "Name is required",
});
} else {
let newObj = omit(errors, "first");
setErrors(newObj);
}
break;
default:
break;
}
};
const handleChange = (event) => {
event.persist();
let name = event.target.name;
let val = event.target.value;
validate(event, name, val);
setValues({
...values,
[name]: val,
});
};
const handleSubmit = (event) => {
if (event) event.preventDefault();
if (
Object.keys(errors).length === 0 &&
Object.keys(values).length !== 0 &&
values.code &&
values.first
) {
callback();
} else {
if (!values.code) {
setErrors({
...errors,
code: "Code is required.",
});
}
if (!values.first) {
setErrors({
...errors,
first: "Name is required.",
});
}
}
};
return {
values,
errors,
handleChange,
handleSubmit
};
};
export default useForm;
您可以通过只有一个验证例程来简化您的代码。您可以修复您提到的错误,一次只有一个错误,方法是使用框架传递给 setState
的当前状态。这个的构造是
setState(e => /* whatever you want to return relative to e */);
最终,您看到的错误是因为组件及其代码在每次状态更改后都会重新呈现(但出于性能原因,它们是批处理的),因此您的代码只能看到旧版本的直接访问它时的状态。如果您在更改它时想要实际的当前状态,最好让框架将它直接传递给更改状态函数。如果你一直遵循这个模式,你很少会遇到 React 状态模型的任何问题。
然而,困难的问题不是这些。困难的问题是您消除错误的方式。首先,从 UI 的角度来看,仅在编辑时将字段显示为错误有点不一致,因为表单在首次显示时是错误的。但是,忽略这一点,潜在的技术问题是从对象中删除 属性 。通常我们会在需要这样做时使用数组而不是对象,或者我们会使用下划线的 omit
函数。我在你的代码中看到你调用了一个名为 omit
的函数,但你没有解释它是什么。不过,在您的情况下,从不删除 属性 即可轻松解决此问题。相反,将每个对象都作为具有有效标志的对象。这将允许您完全删除 switch 语句。
您可以更进一步,也可以将值滚动到其中,为每个字段创建一个完整的单一状态,但我不会在这里演示。
我还没有测试过这个,所以这里可能有一两个错别字。
const useForm = (callback) => {
const [values, setValues] = useState({code: '', first: ''});
const [errors, setErrors] = useState({first: {message: "Name is required", valid: true}, code: {message: "Code is required", valid: true}});
const isEmpty = val => val.trim() === "" || val.trim() === null);
const validate = (name, val) => {
setErrors(e => ({...e, [name]: {...e[name], valid: isEmpty(val)}}))
return errors[name].valid;
};
const handleChange = (event) => {
let name = event.target.name;
let val = event.target.value;
setValues({...values, [name]: val});
validate(name, val);
};
const handleSubmit = event => {
if (event) event.preventDefault();
const valid = validate('code', values.code) && validate('first', values.first);
if ( valid ) {
callback();
}
};
return {
values,
errors,
handleChange,
handleSubmit
};
};
export default useForm;
而且,您必须稍微更改 HTML 模板:
{!errors.first.valid && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>{errors.first.message}</p>}
我意识到这个新的 validate
函数与您原来的函数有很大的不同,但根据您自己的代码,我认为您可以处理它。基本上,它只是使用解构和变量 属性 访问器。这看起来在您的理解能力范围内,但如果有任何混淆或实际上不起作用,请随时提出更多问题 :)
我的 React 应用程序有问题。我有一个带有两个输入的表单,当我提交带有空输入的表单时,它应该在每个输入中呈现一条错误消息。问题是它不显示第一个输入。我怎样才能修复它以在每个错误中显示错误?实现在 useForm.js
.
FormUI图片:
我的代码:
Form.js
const Form = () => {
const formLogin = () => {
console.log("Callback function when form is submitted!");
console.log("Form Values ", values);
}
const {handleChange, values, errors, handleSubmit} = useForm(formLogin);
return (
<Wrapper>
<form onSubmit={handleSubmit}>
<div className="govgr-form-group gap-bottom">
<label className="govgr-label govgr-!-font-weight-bold" htmlFor="code">Code*</label>
{errors.code && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>{errors.code}</p>}
<input className={`govgr-input govgr-!-width-three-quarter ${errors.code ? 'govgr-error-input' : ''}`} id="code" name="code" type="text" onChange={handleChange} />
</div>
<fieldset>
<div className="govgr-form-group">
<label className="govgr-label govgr-!-font-weight-bold" htmlFor="first">Name*</label>
{errors.first && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>{errors.first}</p>}
<input className={`govgr-input govgr-!-width-three-quarter ${errors.first ? 'govgr-error-input' : ''}`} id="first" name="first" type="text" onChange={handleChange} />
</div>
</fieldset>
<button type="submit" className="govgr-btn govgr-btn-primary btn-center">Save</button>
</form>
</Wrapper>
);
};
export default Form;
useForm.js
:
const useForm = (callback) => {
const [values, setValues] = useState({});
const [errors, setErrors] = useState({});
const validate = (event, name, value) => {
event.persist();
switch (name) {
case "code":
if (value.trim() === "" || value.trim() === null) {
setErrors({
...errors,
code: "Code is required",
});
} else {
let newObj = omit(errors, "code");
setErrors(newObj);
}
break;
case "first":
if (value.trim() === "" || value.trim() === null) {
setErrors({
...errors,
first: "Name is required",
});
} else {
let newObj = omit(errors, "first");
setErrors(newObj);
}
break;
default:
break;
}
};
const handleChange = (event) => {
event.persist();
let name = event.target.name;
let val = event.target.value;
validate(event, name, val);
setValues({
...values,
[name]: val,
});
};
const handleSubmit = (event) => {
if (event) event.preventDefault();
if (
Object.keys(errors).length === 0 &&
Object.keys(values).length !== 0 &&
values.code &&
values.first
) {
callback();
} else {
if (!values.code) {
setErrors({
...errors,
code: "Code is required.",
});
}
if (!values.first) {
setErrors({
...errors,
first: "Name is required.",
});
}
}
};
return {
values,
errors,
handleChange,
handleSubmit
};
};
export default useForm;
您可以通过只有一个验证例程来简化您的代码。您可以修复您提到的错误,一次只有一个错误,方法是使用框架传递给 setState
的当前状态。这个的构造是
setState(e => /* whatever you want to return relative to e */);
最终,您看到的错误是因为组件及其代码在每次状态更改后都会重新呈现(但出于性能原因,它们是批处理的),因此您的代码只能看到旧版本的直接访问它时的状态。如果您在更改它时想要实际的当前状态,最好让框架将它直接传递给更改状态函数。如果你一直遵循这个模式,你很少会遇到 React 状态模型的任何问题。
然而,困难的问题不是这些。困难的问题是您消除错误的方式。首先,从 UI 的角度来看,仅在编辑时将字段显示为错误有点不一致,因为表单在首次显示时是错误的。但是,忽略这一点,潜在的技术问题是从对象中删除 属性 。通常我们会在需要这样做时使用数组而不是对象,或者我们会使用下划线的 omit
函数。我在你的代码中看到你调用了一个名为 omit
的函数,但你没有解释它是什么。不过,在您的情况下,从不删除 属性 即可轻松解决此问题。相反,将每个对象都作为具有有效标志的对象。这将允许您完全删除 switch 语句。
您可以更进一步,也可以将值滚动到其中,为每个字段创建一个完整的单一状态,但我不会在这里演示。
我还没有测试过这个,所以这里可能有一两个错别字。
const useForm = (callback) => {
const [values, setValues] = useState({code: '', first: ''});
const [errors, setErrors] = useState({first: {message: "Name is required", valid: true}, code: {message: "Code is required", valid: true}});
const isEmpty = val => val.trim() === "" || val.trim() === null);
const validate = (name, val) => {
setErrors(e => ({...e, [name]: {...e[name], valid: isEmpty(val)}}))
return errors[name].valid;
};
const handleChange = (event) => {
let name = event.target.name;
let val = event.target.value;
setValues({...values, [name]: val});
validate(name, val);
};
const handleSubmit = event => {
if (event) event.preventDefault();
const valid = validate('code', values.code) && validate('first', values.first);
if ( valid ) {
callback();
}
};
return {
values,
errors,
handleChange,
handleSubmit
};
};
export default useForm;
而且,您必须稍微更改 HTML 模板:
{!errors.first.valid && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>{errors.first.message}</p>}
我意识到这个新的 validate
函数与您原来的函数有很大的不同,但根据您自己的代码,我认为您可以处理它。基本上,它只是使用解构和变量 属性 访问器。这看起来在您的理解能力范围内,但如果有任何混淆或实际上不起作用,请随时提出更多问题 :)