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 等于 errorerrorLogin.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 等于传递给ZFormerror 的值,即无或undefined

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

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

您现在将看到password: "test" 设置为ZForm 的初始状态。

如果您希望能够在Login.jsZForm 之间共享错误,则应删除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 中使用errorsZForm 将简单地接受错误作为参数并将其传递下去:

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)

Python文摘:Python with Context Managers