Почему иногда render вызывается сразу после setState, а иногда нет



Вот мои коды:



class TestState extends Component{
constructor(props){
super(props);
this.state={
text:"123"
}
this._hello = this._hello.bind(this)
}

_hello(){
console.log("guangy will set state...");
let num = Math.floor(Math.random()*100);
this.setState({text: ""+num });
console.log("guangy after set state...");
}

componentWillUpdate(){
console.log("guangy componentWillUpdate")
}

render(){
console.log("guangy render.....")
return(<View>
<Text>{this.state.text}</Text>
<Button title="click" onPress={
()=>{
this._hello();
}
}/>
<Button title="click1" onPress={
()=>{
setTimeout(()=>{
this._hello();
}, 10);
}
}/>
</View>);
}
}


Когда я нажал первую кнопку, журнал выглядит следующим образом:



guangy will set state...
guangy after set state...
guangy componentWillUpdate
guangy render.....


И регистрирует при нажатии на вторую кнопку:



guangy will set state...
guangy componentWillUpdate
guangy render.....
guangy after set state...


Я думаю, что функция рендеринга должна называться асинхронной, и на самом деле в большинстве случаев это так, например, когда я нажал первую кнопку, но когда я нажал вторую кнопку, функция рендеринга, кажется, называется синхронной, потому что журнал "after set state" печатается после журнала "render". почему это происходит?

592   1  

1 ответ:

Согласно документам

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

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

Реагировать может задержать его

Выполнении функция setState() не всегда немедленно обновить компонент.

Он может пакетировать или отложить обновление до более позднего времени.

Так должны ли мы думать, что setState может быть или не быть асинхронной операцией?
Ну да.

Это setState взято из исходного кода:

ReactComponent.prototype.setState = function(partialState, callback) {
  invariant(
    typeof partialState === 'object' ||
    typeof partialState === 'function' ||
    partialState == null,
    'setState(...): takes an object of state variables to update or a ' +
    'function which returns an object of state variables.'
  );
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};

Выглядит как обычная синхронная функция, ну на самом деле setState, поскольку она сама по себе не асинхронна, но эффект от ее выполнения может быть.

Я провел некоторое тестирование и понял, что react будет обновлять состояние только асинхронно, когда react контролирует весь поток, где react не может контролировать поток, что означает, что контекст выполнения находится вне его, то react обновит состояние немедленно.

Что же тогда может взять под контроль react?
Такие вещи, как setTimeout, setInterval ajax-запрос и другие webApi.
Даже обработчик событий, подключенный извне react, вызовет такое поведение.

На самом деле вот небольшой фрагмент, чтобы удовлетворить наш эксперимент.
У меня есть компонент React App, который имеет состояние с ключом myValue и метод с именем onClick.
Этот метод регистрирует текущее состояние, затем вызывает setstate, а затем снова регистрирует состояние.

App визуализация 3 элементов:

  • текущее значение myValue.
  • кнопка, которую мы прикрепляем onClick через react API.
  • кнопка, которую мы прикрепляем onClick через addEventListener API (с другой стороны, не обращайте внимания).

Когда мы нажимаем на первую кнопку, когда react контролирует поток, состояние обновляется асинхронно.

Когда мы нажмем вторую кнопку, react немедленно обновит состояние.

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      myVal: 0
    }
  }

  componentDidMount() {
    const el = document.getElementById('btn');
    el.addEventListener('click', this.onClick);
  }

  onClick = () => {
    console.log('Pre setState', this.state.myVal);
    this.setState({ myVal: this.state.myVal + 1 });
    console.log('Post setState', this.state.myVal);
    console.log('-------------------');
  }


  render() {
    const { myVal } = this.state;
    return (
      <div>
        <div>{myVal}</div>
        <button onClick={this.onClick}>Managed by react</button>
        <button id='btn'>Not Managed by react</button>
      </div>);
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Comments

    Ничего не найдено.