setState()의 비동기 특성(React js)

5098 단어 react
요점:
React는 업데이트를 일괄 처리하고 프레임당 한 번 플러시합니다(성능 최적화). eventListeners, Ajax, setTimeout 및 유사한 웹 API
주요 아이디어
setState()는 this.state를 즉시 변경하지 않지만 보류 중인 상태 전환을 생성합니다. 이 메서드를 호출한 후 this.state에 액세스하면 잠재적으로 기존 값을 반환할 수 있습니다. setState에 대한 호출의 동기 작업이 보장되지 않으며 성능 향상을 위해 호출을 일괄 처리할 수 있습니다.
아래 코드를 실행하면 다음을 관찰할 수 있습니다.
모든 상황(addEventListener, setTimeout 또는 AJAX 호출)에서 이전 상태와 이후 상태가 다른 것을 볼 수 있습니다. 그리고 그 렌더링은 setState 메서드를 트리거한 직후에 호출되었습니다. 그런데 왜 그럴까요? 글쎄, React는 이해하지 못하므로 라이브러리 내부에 있지 않은 코드를 제어할 수 없습니다. 예를 들어 타임아웃 또는 AJAX 호출은 React 컨텍스트 외부에서 실행되는 개발자 작성 코드입니다.
그렇다면 왜 이런 경우에 React가 상태를 동기식으로 업데이트할까요? 글쎄, 그것은 가능한 한 방어적이 되려고 하기 때문입니다. 제어할 수 없다는 것은 성능 최적화를 수행할 수 없음을 의미하므로 즉시 상태를 업데이트하고 뒤따르는 코드가 사용 가능한 최신 정보에 액세스할 수 있는지 확인하는 것이 좋습니다.

class TestComponent extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = {
      dollars: 10
    };
    this._saveButtonRef = (btn => { this._btnRef = btn });
    [
      '_onTimeoutHandler',
      '_onMouseLeaveHandler',
      '_onClickHandler',
      '_onAjaxCallback',
    ].forEach(propToBind => {
      this[propToBind] = this[propToBind].bind(this);
    });
  }

  componentDidMount() {
    // Add custom event via `addEventListener`
    //
    // The list of supported React events does include `mouseleave`
    // via `onMouseLeave` prop
    //
    // However, we are not adding the event the `React way` - this will have
    // effects on how state mutates
    //
    // Check the list here - https://reactjs.org/docs/events.html
    this._btnRef.addEventListener('mouseleave', this._onMouseLeaveHandler);

    // Add JS timeout
    //
    // Again,outside React `world` - this will also have effects on how state
    // mutates
    setTimeout(this._onTimeoutHandler, 10000);

    // Make AJAX request
    fetch('https://api.github.com/users')
      .then(this._onAjaxCallback);
  }

  render() {
    console.log('State in render: ' + JSON.stringify(this.state));

    return (
       <button
         ref={this._saveButtonRef}
         onClick={this._onClickHandler}>
         'Click me'
      </button>
    );
  }

  _onClickHandler() {
    console.log('State before (_onClickHandler): ' + JSON.stringify(this.state));
    this.setState({
      dollars: this.state.dollars + 10
    });
    console.log('State after (_onClickHandler): ' + JSON.stringify(this.state));
  }

  _onMouseLeaveHandler() {
    console.log('State before (mouseleave): ' + JSON.stringify(this.state));
    this.setState({
      dollars: this.state.dollars + 20
    });
    console.log('State after (mouseleave): ' + JSON.stringify(this.state));
  }

  _onTimeoutHandler() {
    console.log('State before (timeout): ' + JSON.stringify(this.state));
    this.setState({
      dollars: this.state.dollars + 30
    });
    console.log('State after (timeout): ' + JSON.stringify(this.state));
  }

  _onAjaxCallback(response) {
    if (response.status !== 200) {
      console.log('Error in AJAX call: ' + response.statusText);
      return;
    }
    console.log('State before (AJAX call): ' + JSON.stringify(this.state));
    this.setState({
      dollars: this.state.dollars + 40
    });
    console.log('State after (AJAX call): ' + JSON.stringify(this.state));
  }
};

// Render to DOM
ReactDOM.render(
  <TestComponent />,
  document.getElementById('app')
);


가능한 해결책?

우리는 하나의 매개변수로만 setState를 호출하는 데 익숙하지만 실제로 메서드의 시그니처는 두 개를 지원합니다. 전달할 수 있는 두 번째 인수는 상태가 업데이트된 후 항상 실행될 콜백 함수입니다(React의 알려진 컨텍스트 내부에 있든 외부에 있든 상관 없음).

예를 들면 다음과 같습니다.

_onClickHandler: function _onClickHandler() {
   console.log('State before (_onClickHandler): ' + JSON.stringify(this.state));
   this.setState({
   dollars: this.state.dollars + 10
   }, () => {
   console.log('Here state will always be updated to latest version!');
   console.log('State after (_onClickHandler): ' + JSON.stringify(this.state));
   });
}


setstate의 비동기 특성에 대한 참고 사항

정치적으로 올바르려면 메소드로서의 setState는 항상 동기식입니다. 업데이터의 enqueueState 또는 enqueueCallback과 같이 배후에서 무언가를 호출하는 함수일 뿐입니다.
실제로 다음은 React 소스 코드에서 직접 가져온 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');
  }
};


실제로 동기화 또는 비동기는 조정 알고리즘, VDOM 비교 수행 및 실제 DOM 업데이트를 위한 렌더링 호출과 같은 React 애플리케이션에서 setState를 호출하는 효과입니다.

참조 :
React-bits :디자인 패턴 및 기법

좋은 웹페이지 즐겨찾기