Почему состояние обновляется до последнего сообщения, если я использую оператор распространения?

#javascript #reactjs

#javascript #reactjs

Вопрос:

У меня есть приложение react, в котором есть форма с некоторыми полями вместе с проверкой. Однако проверки не работают так, как я ожидаю, что они будут работать. Код приведен ниже:

 state = {
  user: null,
  errors: {}
}

onSubmitHandler = e => {
  e.preventDefault();
  //Check if first Name is returned empty
  if (this.state.user.name.trim().length === 0) {
    this.setState({
      errors: {
        ...this.state.errors,
        name: "This field is required"
      }
    });
  }
  //Check if last name is returned empty
  if (this.state.user.surname.trim().length === 0) {
    this.setState({
      errors: {
        ...this.state.errors,
        surname: "This field is required"
      }
    });
    return;
  }
}
  

Теперь, когда я не предоставляю оба поля ввода и оставляю их пустыми, я ожидаю, что в моем состоянии будут отображаться оба сообщения об ошибках, т.е. errors: {name: "This field is required", surname: "This field is required"}

Но я получаю только недавнюю ошибку, которая относится к фамилии. Итак, мой результат выглядит так errors: {surname: "This field is required"}

Почему оно переопределяет первое сообщение об ошибке? Любая помощь будет оценена. Код для поля ввода не отображается, поскольку они являются просто тегами ввода с обработчиком событий onChange.

Ответ №1:

Два вызова в будут объединены вместе. this.setState onSubmitHandler

Из React Docs - setState():

Думайте о setState() как о запросе, а не о немедленной команде для обновления компонента. Для лучшей воспринимаемой производительности React может задержать его, а затем обновить несколько компонентов за один проход. React не гарантирует, что изменения состояния будут применены немедленно.

setState() не всегда немедленно обновляет компонент. Это может привести к пакетной передаче или отложению обновления на более поздний срок. Это делает чтение this.state сразу после вызова setState() потенциальной ловушкой

Поэтому, когда вы это делаете ...this.state.errors , this.state.errors в обоих if блоках ссылается на пустой объект. В результате вы видите только surname ключ в errors объекте в состоянии.

Вы можете исправить эту проблему, скопировав this.state.errors объект, добавив ключи в скопированный объект, а затем передать этот скопированный объект this.setState .

 onSubmitHandler = e => {
    e.preventDefault();
    
    // clone the "errors" object
    const errors = { ...this.state.errors };
    
    //Check if first Name is returned empty
    if(this.state.user.name.trim().length === 0) {
         errors.name = "This field is required";
    }
            
    //Check if last name is returned empty
    if(this.state.user.surname.trim().length === 0) {
        errors.surname = "This field is required";
    }

    this.setState({ errors });
}
  

Ответ №2:

Это потому, что во втором распространении все еще используется старое значение this.state.errors, поскольку оно еще не изменилось. Оба setStates могут (в вашем случае: определенно были) обновлять состояние позже, после выполнения onSubmitHandler .

Читайте также https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

Измените аргументы setState на функции, которые автоматически используют последнее состояние:

 onSubmitHandler = e => {
  e.preventDefault();
  //Check if first Name is returned empty
  if (this.state.user.name.trim().length === 0) {
    this.setState(state => ({
      errors: {
        ...state.errors,
        name: "This field is required"
      }
    }));
  }
  //Check if last name is returned empty
  if (this.state.user.surname.trim().length === 0) {
    this.setState(state => ({
      errors: {
        ...state.errors,
        surname: "This field is required"
      }
    }));
    return;
  }
}