具有多个状态值的React Context不更新初始值



我正在构建一个通用的表单上下文来帮助管理我的表单状态。虽然一切都在工作,我不能得到我的错误对象初始设置在我的形式。看起来只设置了一个状态,而不是两个。从我使用过的其他组件中,我不知道在一个功能组件中只能有一个useState ?

import React, { useState } from "react";
export const FormContext = React.createContext();
// sample initialErrors looks like {password: "Error"}
const ZForm = ({ children, initialValues, initialErrors }) => {
const [errors, setErrors] = useState(initialErrors);  // sets as expected.
const [form, setForm] = useState(initialValues);  // initial values not set.
console.log("ZForm Intial Err:", initialErrors);
console.log("ZForm State Err:", errors); // always null!
const handleFormChange = (event, validators) => {
const { name, value } = event.target;
setForm({ ...form, [name]: value });
};
return (
<form>
<FormContext.Provider
value={{
form,
errors,
handleFormChange,
}}
>
{children}
</FormContext.Provider>
</form>
);
};
export default ZForm;

我的Login组件启动这个表单来测试

function Login() {
const [error, setError] = useState();
function handleLogin({ email, password }) {
setError({ password: "test" }); // simulate an error
}
console.log("Login Erros", error); // confirms error is set on submit.
return (
<div className="page" style={{ justifyContent: "center" }}>
<div className="inlineForm">
<h3>Login</h3>
<ZForm
initialValues={{ email: "", password: "" }}
initialErrors={error}
>
<Grid container spacing={4}>
<Grid item xs={12} md={12}>
<ZTextField label="Email" name="email"></ZTextField>
</Grid>
<Grid item xs={12} md={12}>
<ZTextField label="Password" name="password"></ZTextField>
</Grid>
</Grid>
<Grid item xs={12}>
<FormActionButtons
onSave={handleLogin}
//         disabled={isLoading}
></FormActionButtons>
</Grid>
</ZForm>
</div>
</div>
);
}
export default Login;

ZForm组件的initialState为空,因为您没有向initialErrorsprop传递任何内容。与获取对象的initialValuesprop相反。

error在此组件:

<ZForm
initialValues={{ email: "", password: "" }}
initialErrors={error}
>

在这一行被定义为空:

const [error, setError] = useState();

useState(initialErrors)只在组件渲染时使用一次initialErrors。在组件渲染完成后的任何时间更改该值都不会影响状态。在组件呈现时,initialErrors的值等于传递给ZFormerror的值,即none或undefined

如果您将Login.js中的useState更改为:

const [error, setError] = useState({password: "test"});

你现在会看到{password: "test"}设置在ZForm的初始状态。

如果您希望能够在Login.jsZForm之间共享错误,您应该从ZForm中删除用于错误的useState,这一行:

const [errors, setErrors] = useState(initialErrors);

相反,将Login.js中的ZForm组件上的initialErrorsprop称为errors,并在组件中使用该值。

Login.js:

<ZForm
initialValues={{ email: "", password: "" }}
errors={errors}
>

ZForm.js:

const ZForm = ({ children, initialValues, errors }) => {

初始值表示" first"在这种情况下。它在第一次调用时被赋值给该值,并且永远被忽略在那之后,不管它是否改变。考虑:

const [number] = useState(Math.random());

number的值永远不会改变,因为它只考虑参数一次。

在您的情况下,您有两个不同的,不同的地方存储errors:一个在Login中,另一个在ZForm中。沙箱中的检测错误显示ZForm中的setErrors函数未使用,该函数是更新ZForm中的值的唯一方法。

简而言之,您应该选择其中一个作为记录的来源:


使用Login中的errors,ZForm将简单地接受错误作为参数并将其传递下去:

const ZForm = ({ children, initialValues, errors }) => {
const [form, setForm] = useState(initialValues);
console.log("ZForm State Err:", errors); // whatever is passed in
// ...

ZForm中存储错误是一个小技巧,因为要在Login中使用它,表单必须将包裹在周围。Login组件,而不是嵌套在其中。如果你想使用ZForm中的一个,你必须将setErrors添加到FormContext.Provider值中,但请注意,由于ZFormLogin中呈现,除非你将其包装在其他组件中,否则你将无法在那里引用它,例如:

const ZForm = ({ children, initialValues, initialErrors }) => {
const [form, setForm] = useState(initialValues); // initial values not set.
const [errors, setErrors] = useState(initialErrors); // sets as expected.
console.log("ZForm Intial Err:", initialErrors);
console.log("ZForm State Err:", errors);
// NOT SAFE! See comments below
const handleFormChange = (event, validators) => {
const { name, value } = event.target;
setForm({ ...form, [name]: value });
};
return (
<form>
<FormContext.Provider
value={{
form,
errors,
setErrors,
handleFormChange
}}
>
{children}
</FormContext.Provider>
</form>
);
};
function Login() {
// use the `setErrors` from `ZForm`'s context
const { errors, setErrors } = useContext(FormContext);
function handleLogin({ email, password }) {
setErrors((errors) => ({
...errors,
password: "test",
})); // simulate an error
}
return (
<div className="page" style={{ justifyContent: "center" }}>
<div className="inlineForm">
<h3>Login</h3>
{/* form content goes here... */}
</div>
</div>
);
}
// Wrap `ZForm` around the `Login` form:
export default () => (
<ZForm initialValues={{ email: "", password: "" }}>
<Login />
</ZForm>
);

React中的表单很棘手。它们只是不太适合React的自顶向下模型。也有一些微妙的bug,比如handleFormChange,它应该使用setForm的函数参数,而不是直接引用form(参见:https://reactjs.org/docs/hooks-reference.html#functional-updates)。我强烈建议使用库,而不是自己编写库。还有许多其他的,但我发现一个相对直观的是https://www.npmjs.com/package/react-hook-form

相关内容

  • 没有找到相关文章

最新更新