使用带有钩子的 React.memo 来控制输入

Posted

技术标签:

【中文标题】使用带有钩子的 React.memo 来控制输入【英文标题】:Using React.memo with hooks for controlled inputs 【发布时间】:2019-10-12 15:11:28 【问题描述】:

我正在通过自定义 React 钩子提供一些表单功能。这个钩子有一些类似于 Formik 的功能(但这确实是基本的东西)。

function useFormValidation(initialState, validate) 
  const [values, setValues] = React.useState(initialState);
  const [errors, setErrors] = React.useState();
  const [isSubmitting, setSubmitting] = React.useState(false);

  React.useEffect(() => 
    if (isSubmitting) 
      const noErrors = Object.keys(errors).length === 0;
      if (noErrors) 
        console.log("authenticated!", values.email, values.password);
        setSubmitting(false);
       else 
        setSubmitting(false);
      
    
  , [errors]);

  function handleChange(event) 
    setValues(
      ...values,
      [event.target.name]: event.target.value
    );
  

  function handleBlur() 
    const validationErrors = validate(values);
    setErrors(validationErrors);
  

  function handleSubmit(event) 
    event.preventDefault();
    const validationErrors = validate(values);
    setErrors(validationErrors);
    setSubmitting(true);
  

  return 
    handleSubmit,
    handleChange,
    handleBlur,
    values,
    errors,
    isSubmitting
  ;




表格如下:

function Register() 
  const 
    handleSubmit,

    handleChange,
    handleBlur,
    values,
    errors,
    isSubmitting
   = useFormValidation(INITIAL_STATE, validateAuth);
  // const [email, setEmail] = React.useState("");
  // const [password, setPassword] = React.useState("");

  return (
    <div className="container">
      <h1>Register Here</h1>
      <form onSubmit=handleSubmit>
        <Input
          handleChange=handleChange
          handleBlur=handleBlur
          name="email"
          value=values.email
          className=errors.email && "error-input"
          autoComplete="off"
          placeholder="Your email address"
        />
        errors.email && <p className="error-text">errors.email</p>
        <Input
          handleChange=handleChange
          handleBlur=handleBlur
          value=values.password
          className=errors.password && "error-input"
          name="password"
          // type="password"
          placeholder="Choose a safe password"
        />
        errors.password && <p className="error-text">errors.password</p>
        <div>
          <button disabled=isSubmitting type="submit">
            Submit
          </button>
        </div>
      </form>
    </div>
  );

接下来是被记忆的组件:

function Input(
  handleChange,
  handleBlur,
  name,
  value,
  className,
  autoComplete,
  placeholder,
  type
) 

  return (
    <input
      onChange=handleChange
      onBlur=handleBlur
      name=name
      value=value
      className=className
      autoComplete=autoComplete
      placeholder=placeholder
      type=type
    />
  );


function areEqual(prevProps, nextProps) 
  console.log(`
    prevProps: $JSON.stringify(prevProps.value)
    nextProps: $JSON.stringify(nextProps.value)
  `);
  return prevProps.value === nextProps.value;

const useMemo = (component, propsAreEqual) => 
  return memo(component, propsAreEqual);
;
export default useMemo(Input, areEqual);

我在第一个输入中输入了一些文本。然后,当我切换到第二个输入并开始输入时,第一个输入会丢失值。就像表单没有呈现 LAST MEMOIZED 输入,而是呈现以前的版本。 我是 React 初学者,无法找出解决方案。有什么帮助吗?

【问题讨论】:

【参考方案1】:

尝试使用 setState 的更新形式,它接受一个函数:

function handleChange(event) 
  // event.target wont be available when fn is run in setState
  // so we save them in our own local variables here
  const  name, value  = event.target;

  setValues(prev => (
    ...prev,
    [name]: value
  ));

【讨论】:

非常感谢您的回复!根据您的建议,我最终阅读了一些相关的内容。我正在阅读此beware-react-setstate-is-asynchronous,但可能并非如此。 (或者是吗?)你能给我一些进一步阅读的链接吗? 这可能非常有用 [freecodecamp.org/news/… setState)【参考方案2】:

您的 areEqual 方法转换为

仅当 value 更改时重新呈现我的输入。

但实际上,您的挂钩中的handleChange 函数也在发生变化。此外,您对两个输入使用相同的 handleChange。因此,Input“记住”只有上次value 发生变化的handleChange,并且由于handleChange 通过关闭跟踪values,它反过来“记住”values已创建。

更改您的areEqual 方法(或完全省略它)以验证handleChange 的更改,将解决您的问题。

function areEqual(prevProps, nextProps) 
  return (
    prevProps.value === nextProps.value &&
    prevProps.handleChange === nextProps.handleChange
  );

解决方案here的代码框

【讨论】:

"handleChange" 不是 Input 道具的一部分! “handleChange”是钩子的一部分。所以不可能有“prevProps.handleChange === nextProps.handleChange”。 @JoseMiguelOchoa Input 显然有一个 handleChange 属性。你试过我的解决方案了吗? 感谢您的回复。我现在明白你在说什么了。我试过你的解决方案。我应该提到我使用记忆输入,因为表单需要避免不必要的渲染。在我的真实项目形式中,我有很多选择和大量输入。如果我不记住输入和选择,它会使表单真正无响应(每个选择都使用 Apollo 从 Graphql 端点获取数据)。 当前实现中可能会出现许多细微的错误。如果 value 以外的任何属性发生更改,您的 Input 组件将不会重新渲染。现在可能会成功,但你以后永远不会知道。请记住这一点。 感谢您的建议!我会做的更一般,所以它可以接受更多的值。谢谢。

以上是关于使用带有钩子的 React.memo 来控制输入的主要内容,如果未能解决你的问题,请参考以下文章

使用React.memo()来优化函数组件的性能

使用React.memo()来优化函数组件的性能

使用React.memo()来优化函数组件的性能

使用React.memo()来优化函数组件的性能

使用 react-redux、reselect 和 React.memo() 来记忆功能组件

在表单控制输入中反应 js useReducer 钩子