React Context with Multiple State Values 不更新初始值
Posted
技术标签:
【中文标题】React Context with Multiple State Values 不更新初始值【英文标题】:React Context with Multiple State Values not updating initial values 【发布时间】:2021-12-14 14:23:10 【问题描述】:我正在构建一个通用表单上下文来帮助管理我的表单状态。虽然一切正常,但我无法将我的 Errors 对象设置为表单上的初始设置。看起来只设置了一个状态,而不是两者。从我使用过的其他组件中,我不知道功能组件中只能有一个 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;
我的登录组件启动此表单进行测试
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;
【问题讨论】:
你能添加可重现的例子吗?您在 cmets 中给出了混合信息。您说错误已定义,而在下方您说它为空? 代码示例:codesandbox.io/s/dreamy-firefly-i64zf?file=/src/ZForm.js 如果您将 useState(initialErrors) 移动到该文件的顶部,您将看到错误但会丢失表单值。 也许我误解了这个问题,但看起来你没有将任何东西传递给你的ZForm
组件 initialErrors
道具。 initialErrors
等于 error
但 error
在 Login.js
内部被初始化,useState()
没有默认值。 Login.js
中的error
的值仅在调用handleLogin()
时更新。所以ZForm
的初始状态将是空或undefined
,因为这是initialState
首次渲染ZForm
组件时的值。
【参考方案1】:
ZForm
组件的 initialState
是空的,因为您没有向 initialErrors
属性传递任何内容。与获取对象的 initialValues
属性相反。
error
在这个组件中:
<ZForm
initialValues= email: "", password: ""
initialErrors=error
>
在此行中被定义为空:
const [error, setError] = useState();
useState(initialErrors)
只在组件渲染时使用一次initialErrors
。在组件渲染后的任何时候更改值都不会影响状态。在组件渲染时,initialErrors
等于传递给ZForm
的error
的值,即无或undefined
。
如果您将Login.js
中的useState
更改为:
const [error, setError] = useState(password: "test");
您现在将看到password: "test"
设置为ZForm
的初始状态。
如果您希望能够在Login.js
和ZForm
之间共享错误,则应删除useState
中的错误ZForm
,此行:
const [errors, setErrors] = useState(initialErrors);
相反,只需在Login.js
中调用ZForm
组件上的initialErrors
属性errors
,并在您的组件中使用该值。
Login.js
:
<ZForm
initialValues= email: "", password: ""
errors=errors
>
ZForm.js
:
const ZForm = ( children, initialValues, errors ) =>
【讨论】:
【参考方案2】:在这种情况下,首字母的意思是“第一个”。它在第一次调用时分配给该值,然后永远忽略,无论它是否改变。考虑:
const [number] = useState(Math.random());
number
的值永远不会改变,因为它只考虑参数一次。
在您的情况下,您有 2 个不同的、不同的位置来存储 errors
:一个在 Login
,另一个在 ZForm
。沙盒中的 linting 错误显示 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
值,但请注意,由于ZForm
呈现在Login
内部,因此您将无法引用除非你把它包装在另一个组件中,例如:
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 自上而下的模型。还有一些细微的错误,例如您的handleFormChange
,它应该使用setForm
的函数参数,而不是直接引用form
(请参阅:https://reactjs.org/docs/hooks-reference.html#functional-updates)。我强烈建议为此使用库,而不是自己滚动。还有很多其他的,但我发现一个比较直观的是https://www.npmjs.com/package/react-hook-form
【讨论】:
以上是关于React Context with Multiple State Values 不更新初始值的主要内容,如果未能解决你的问题,请参考以下文章
React Context with Hooks 总是“落后一步”
React Context with Multiple State Values 不更新初始值
React Native with Context API 警告:“允许需要循环,但可能导致未初始化的值......”
java 来自http://stackoverflow.com/questions/23720140/using-factory-pattern-with-multiple-arguments-in-
[Redux] Generating Containers with connect() from React Redux (FooterLink)