Ошибка "Не удалось обновить во время существующего перехода состояния" в React

Я пытаюсь сделать шаг 15 этого учебника ReactJS: React.js Введение для людей, которые знают, что достаточно JQuery для получения информации

Автор рекомендует следующее:

overflowAlert: function() {
  if (this.remainingCharacters() < 0) {
    return (
      <div className="alert alert-warning">
        <strong>Oops! Too Long:</strong>
      </div>
    );
  } else {
    return "";
  }
},

render() {
  ...

  { this.overflowAlert() }

  ...
}

Я пробовал сделать следующее (что выглядит хорошо для меня):

// initialized "warnText" inside "getInitialState"


overflowAlert: function() {
  if (this.remainingCharacters() < 0) {
    this.setState({ warnText: "Oops! Too Long:" });
  } else {
    this.setState({ warnText: "" });
  }
},

render() {
  ...

  { this.overflowAlert() }
  <div>{this.state.warnText}</div>

  ...
}

И я получил следующую ошибку в консоли в Chrome Dev Tools:

Невозможно обновить во время существующего перехода состояния (например, внутри render или другого конструктора компонента). Методы визуализации должны быть чистая функция реквизита и состояния; побочные эффекты конструктора anti-pattern, но может быть перенесен на componentWillMount.

Здесь JSbin demo. Почему мое решение не работает и что означает эта ошибка?

Ответ 1

Ваше решение работает, потому что оно не имеет смысла логически. Ошибка, которую вы получаете, может быть немного туманной, поэтому позвольте мне сломать ее. В первой строке указано:

Невозможно обновить во время существующего перехода состояния (например, в рендере или другом конструкторе компонента).

Когда обновляется состояние компонента React, компонент переизлучается в DOM. В этом случае возникает ошибка, потому что вы пытаетесь вызвать overflowAlert внутри render, который вызывает setState. Это означает, что вы пытаетесь обновить состояние в рендере, которое будет затем вызывать рендеринг и overflowAlert, а также обновить состояние и повторно выполнить рендеринг и т.д., Что приведет к бесконечному циклу. Ошибка сообщает вам, что вы пытаетесь обновить состояние в результате обновления состояния в первую очередь, что приводит к циклу. Вот почему это не разрешено.

Вместо этого возьмите другой подход и вспомните, что вы пытаетесь выполнить. Вы пытаетесь дать предупреждение пользователю при вводе текста? Если это случай, установите overflowAlert в качестве обработчика события ввода. Таким образом, состояние будет обновляться, когда произойдет событие ввода, и компонент будет перезаписан.

Ответ 2

Вместо выполнения какой-либо задачи, связанной с компонентом в методе рендеринга, сделайте это после обновления компонента В этом случае переход с экрана Splash на другой экран выполняется только после вызова метода componentDidMount.

import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Button,
Image,
} from 'react-native';


let timeoutid;

export default class Splash extends Component {

static navigationOptions = {
navbarHidden: true,
tabBarHidden: true,
};

constructor(props) {
super(props)
this.state = { navigatenow: false };

 }
 componentDidMount() {
 timeoutid=setTimeout(() => {
   this.setState({ navigatenow: true });
 }, 5000);
}
 componentWillUnmount(){

 clearTimeout(timeoutid);
}
componentDidUpdate(){
const { navigate,goBack } = this.props.navigation;

if (this.state.navigatenow == true) {

  navigate('Main');
}
}
render() {

//instead of writing this code in render write this code in 
 componenetDidUdpate method 
/* const { navigate,goBack } = this.props.navigation;

if (this.state.navigatenow == true) {

  navigate('Main');
}*/

return (

  <Image style={{
    flex: 1, width: null,
    height: null,
    resizeMode: 'cover'
  }} source={require('./login.png')}>
  </Image>
);

  }
}

Ответ 3

Убедитесь, что вы используете правильное выражение. Например, используя:

<View onPress={this.props.navigation.navigate('Page1')} />

отличается от

<View onPress={ () => this.props.navigation.navigate('Page1')} />

или

<View onPress={ () => {
    this.props.navigation.navigate('Page1')
}} />

Два последних выше - выражение функции, первое - нет. Убедитесь, что вы передаете функциональный объект в выражение функции () => {}