admin管理员组

文章数量:1398187

I'm providing some form functionality from a custom React hook. This hook has some functionality similar to Formik (but this is really basic stuff).

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
  };
}



The form is the following:

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>
  );
}

And the memoized ponent is the next:

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 = (ponent, propsAreEqual) => {
  return memo(ponent, propsAreEqual);
};
export default useMemo(Input, areEqual);

I enter some text into the first input. Then, when I switch to the second Input and start typing, the first input loses the value. It's like the form is not rendering the LAST MEMOIZED input, but prior versions instead. I'm a React beginner and can't figure out the solution. Any help please?

I'm providing some form functionality from a custom React hook. This hook has some functionality similar to Formik (but this is really basic stuff).

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
  };
}



The form is the following:

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>
  );
}

And the memoized ponent is the next:

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 = (ponent, propsAreEqual) => {
  return memo(ponent, propsAreEqual);
};
export default useMemo(Input, areEqual);

I enter some text into the first input. Then, when I switch to the second Input and start typing, the first input loses the value. It's like the form is not rendering the LAST MEMOIZED input, but prior versions instead. I'm a React beginner and can't figure out the solution. Any help please?

Share Improve this question edited May 27, 2019 at 4:22 hashed_name 5517 silver badges22 bronze badges asked May 27, 2019 at 3:29 Jose Miguel OchoaJose Miguel Ochoa 831 silver badge5 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

Try using the updater form of setState which takes a function:

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
  }));
}

Your areEqual method translates to

Re-render my Input ONLY when the value changes.

But in reality, your handleChange function from the hook is also changing. Also, you use the same handleChange for both the inputs. So, the Input "remembers" only the handleChange from the last time value had changed and since handleChange is tracking values via closure, it in-turn "remembers" the values when it was created.

Changing your areEqual method (or pletely omitting it) to verify a change in handleChange, will solve your problem.

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

A codesandbox of the solution here

本文标签: javascriptUsing Reactmemo with hooks for controlled inputsStack Overflow