[React 깊이] setState 의 실행 메커니즘

몇 가지 개발 과정 에서 자주 발생 하 는 문제
다음 과 같은 몇 가지 문 제 는 우리 가 실제 개발 에서 자주 만 나 는 장면 이다. 다음은 몇 가지 간단 한 예시 코드 로 복원 하 자.
1. setState 는 동기 화 입 니까? 비동기 입 니까? 왜 어떤 때 는 즉시 업데이트 결 과 를 얻 지 못 하고 어떤 때 는 가능 합 니까?
1.1 갈고리 함수 와 React 합성 이벤트 의 setState현재 두 개의 구성 요소 가 있 습 니 다.
  componentDidMount() {
    console.log('parent componentDidMount');
  }

  render() {
    return (
      
); }

구성 요소 내부 에 같은 코드 를 넣 고 Setstate1componentDidMount 에 동기 화 지연 코드 를 넣 고 인쇄 지연 시간:
  componentWillUpdate() {
    console.log('componentWillUpdate');
  }

  componentDidUpdate() {
    console.log('componentDidUpdate');
  }

  componentDidMount() {
    console.log('SetState  setState');
    this.setState({
      index: this.state.index + 1
    })
    console.log('state', this.state.index);
    
    console.log('SetState  setState');
    this.setState({
      index: this.state.index + 1
    })
    console.log('state', this.state.index);
  }

다음은 실행 결과 입 니 다.
설명:
  • 1. 호출 setState 은 즉시 업데이트 되 지 않 습 니 다
  • 2. 모든 구성 요 소 는 같은 업데이트 체 제 를 사용 합 니 다. 모든 구성 요소 didmount 가 있 으 면 부모 구성 요소 didmount 를 사용 한 다음 에 업 데 이 트 를 실행 합 니 다
  • .
  • 3. 업데이트 할 때 각 구성 요소 의 업 데 이 트 를 합 칩 니 다. 각 구성 요 소 는 한 번 만 업 데 이 트 된 수명 주 기 를 촉발 합 니 다.

  • 1.2 비동기 함수 와 원생 사건 중의 setstate?setTimeout 에서 호출 setState (예 는 브 라 우 저 네 이 티 브 이벤트 및 인터페이스 리 셋 에서 실행 효과 와 같 음)
      componentDidMount() {
        setTimeout(() => {
          console.log('  setState');
          this.setState({
            index: this.state.index + 1
          })
          console.log('state', this.state.index);
          console.log('  setState');
          this.setState({
            index: this.state.index + 1
          })
          console.log('state', this.state.index);
        }, 0);
      }

    실행 결과:
    설명:
  • 1. 부모 구성 요소 didmount 에서 실행
  • 2. 호출 setState 동기 업데이트
  • 2. 왜 두 번 연속 setState 한 번 만 효력 이 발생 합 니까?
    다음 코드 를 각각 실행 합 니 다:
      componentDidMount() {
        this.setState({ index: this.state.index + 1 }, () => {
          console.log(this.state.index);
        })
        this.setState({ index: this.state.index + 1 }, () => {
          console.log(this.state.index);
        })
      }
      componentDidMount() {
        this.setState((preState) => ({ index: preState.index + 1 }), () => {
          console.log(this.state.index);
        })
        this.setState(preState => ({ index: preState.index + 1 }), () => {
          console.log(this.state.index);
        })
      }

    실행 결과:
    1
    1
    2
    2

    설명:
  • 1. 대상 을 직접 전달 하 는 setstate 은 한 번 합 쳐 진다
  • .
  • 2. 함수 전달 사용 state 합병 되 지 않 음
  • 2. setState 실행 과정
    소스 코드 가 복잡 하기 때문에 여기에 붙 이지 않 습 니 다. 관심 있 는 것 은 githubclone 한 부 를 올 린 다음 에 아래 의 절차 도 에 따라 한 번 걸 어 갈 수 있 습 니 다.
    1. 흐름 도
    그림 이 잘 모 르 겠 어 요. 클릭 해서 원 도 를 볼 수 있어 요.
  • partialState: setState 들 어 오 는 첫 번 째 매개 변수, 대상 또는 함수
  • _pendingStateQueue: 현재 구성 요소 가 업 데 이 트 를 기다 리 고 있 는 state 대기 열
  • isBatchingUpdates: react 는 현재 대량 업데이트 상태 에 있 는 지, 모든 구성 요소 가 공용
  • dirtyComponent: 현재 업데이트 대기 상태 에 있 는 모든 구성 요소 대기 열
  • transcation: react 의 사무 체 제 는 사무 호출 방법 에 따라 n 개 waper 대상 을 포장 하고 한 번 에 집행 한다. waper.init, 호출 방법, waper.close
  • FLUSH_BATCHED_UPDATES: 업 데 이 트 를 수행 하 는 waper 방법 은 하나 close 밖 에 없다
  • .
    2. 실행 과정
    위의 흐름 도의 문자 설명 을 대조 하면 대략 다음 과 같은 몇 단계 로 나 눌 수 있다.
  • 1. setState 에서 들 어 오 는 partialState 매개 변 수 를 현재 구성 요소 인 스 턴 스 의 state 임시 저장 대기 열 에 저장 합 니 다.
  • 2. 현재 React 가 대량 업데이트 상태 에 있 는 지 판단 합 니 다. 만약 그렇다면 현재 구성 요 소 를 업데이트 할 구성 요소 대기 열 에 추가 합 니 다.
  • 3. 대량 업데이트 상태 에 있 지 않 으 면 대량 업데이트 상태 표 지 를 true 로 설정 하고 트 랜 잭 션 으로 이전 방법 을 다시 호출 하여 현재 구성 요소 가 업데이트 대상 구성 요소 대기 열 에 가입 하도록 합 니 다.
  • 4. 사 무 를 호출 하 는 waper 방법 으로 업 데 이 트 를 기다 리 는 구성 요소 대기 열 을 옮 겨 다 니 며 업 데 이 트 를 순서대로 수행 합 니 다.
  • 5. 수명 주기 componentWillReceiveProps 를 집행 한다.
  • 6. 구성 요소 의 state 임시 저장 대기 열 state 을 합 쳐 최종 업데이트 할 state 대상 을 얻 고 대기 열 을 비 웁 니 다.
  • 7. 라 이 프 사이클 componentShouldUpdate 을 실행 하고 반환 값 에 따라 계속 업데이트 할 지 여 부 를 판단 한다.
  • 8. 수명 주기 componentWillUpdate 를 집행 한다.
  • 9. 진정한 업 데 이 트 를 수행 합 니 다. render
  • 10. 수명 주기 componentDidUpdate 를 집행 한다.

  • 총화
    1. 갈고리 함수 와 합성 이벤트 중:react 의 생명주기 와 합성 사건 에서 react 는 여전히 그의 갱신 메커니즘 에 있 는데 이때 isBranchUpdate 는 true 이다.
    상기 과정 에 따 르 면 이 때 는 몇 번 setState 을 호출 하 든 업 데 이 트 를 실행 하지 않 고 업데이트 할 state 을 저장 _pendingStateQueue 하고 업데이트 할 구성 요 소 를 저장 합 니 다 dirtyComponent.
    지난번 업데이트 메커니즘 이 실행 되면 생명주기 로 예 를 들 면 모든 구성 요소, 즉 최상 위 구성 요소 didmount 를 false 로 설정 합 니 다.이때 이전에 누 적 된 것 isBranchUpdate 을 집행 할 것 이다.
    2. 비동기 함수 와 네 이 티 브 이벤트 중
    실행 체 제 를 보면 setState 자체 가 비동기 가 아니 라 호출 setState 할 때 setState 업데이트 과정 에 있 으 면 현재 업데이트 가 일시 적 으로 저장 되 고 지난번 업데이트 가 실 행 된 후에 실 행 될 때 이 과정 은 비동기 적 인 가상 을 준다.
    라 이 프 사이클 에 서 는 JS 의 비동기 시스템 에 따라 비동기 함 수 를 잠시 저장 하고 모든 동기 코드 가 실 행 된 후에 실 행 됩 니 다. 이때 지난번 업데이트 과정 이 실 행 됐 고 react false 로 설정 되 었 으 며 위의 절차 에 따라 호출 isBranchUpdate 하면 바로 업 데 이 트 를 실행 하여 업데이트 결 과 를 얻 을 수 있 습 니 다.
    3. setState 합병 메커니즘
    다음 프로 세 스 partialState 의 코드 를 보 겠 습 니 다. 이 함 수 는 _processPendingState 임시 저장 대기 열 을 합 친 것 이 고 마지막 으로 합 친 것 state 을 되 돌려 줍 니 다.
    
      _processPendingState: function (props, context) {
        var inst = this._instance;
        var queue = this._pendingStateQueue;
        var replace = this._pendingReplaceState;
        this._pendingReplaceState = false;
        this._pendingStateQueue = null;
    
        if (!queue) {
          return inst.state;
        }
    
        if (replace && queue.length === 1) {
          return queue[0];
        }
    
        var nextState = _assign({}, replace ? queue[0] : inst.state);
        for (var i = replace ? 1 : 0; i < queue.length; i++) {
          var partial = queue[i];
          _assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
        }
    
        return nextState;
      },

    우 리 는 아래 코드 에 만 관심 을 가 져 야 한다.
     _assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);

    대상 이 들 어 오 면 한 번 으로 합 쳐 지 는 것 이 분명 하 다.
    Object.assign(
      nextState,
      {index: state.index+ 1},
      {index: state.index+ 1}
    )

    함수 가 들 어 오 면 함수 의 인자 preState 는 지난번 합병 후의 결과 이기 때문에 계산 결 과 는 정확 합 니 다.
    4. state 호출 componentDidMountcomponentDid Mount () 에서 setState () 를 즉시 호출 할 수 있 습 니 다.추가 렌 더 링 을 실행 하지만 브 라 우 저 에서 화면 을 새로 고치 기 전에 발생 합 니 다.이 경우 render () 가 두 번 호출 되 더 라 도 사용자 가 중간 상 태 를 보지 못 하도록 보장 합 니 다.성능 문 제 를 일 으 키 기 때문에 이 모델 을 신중하게 사용 하 세 요.대부분의 경우 constructor () 에서 할당 초기 상 태 를 사용 하여 대체 할 수 있 습 니 다.그러나 어떤 경우 에는 반드시 이렇게 해 야 한다. 예 를 들 어 모드 상자 와 도구 알림 상자 등 이다.이때, 당신 은 먼저 이 DOM 노드 를 측정 해야만 사이즈 나 위치 에 의존 하 는 어떤 것들 을 과장 할 수 있 습 니 다.
    이상 은 공식 문서 의 설명 입 니 다. setstate 에서 직접 호출 componentDidMount 하 는 것 을 추천 하지 않 습 니 다. 위의 분석 에 따 르 면 setState 자체 가 한 번 의 업데이트 에 있 고 우 리 는 한 번 componentDidMount 을 호출 하면 미래 에 다시 한 번 setState 을 진행 하여 필요 하지 않 은 성능 낭 비 를 초래 하고 대부분 상황 은 초기 값 을 설정 하여 해결 할 수 있 습 니 다.
    물론 render 우 리 는 인 터 페 이 스 를 호출 하고 리 셋 에서 수정 할 수 있다 componentDidMount. 이것 은 정확 한 방법 이다.
    state 초기 값 이 dom 속성 에 의존 할 때 state 에서 componentDidMount 피 할 수 없습니다.
    5. setState componentWillUpdate
    이 두 생명 주기 중 에는 호출 할 수 없다 componentDidUpdate.
    위의 흐름 도 에서 쉽게 발견 할 수 있 는데, 그 안에서 호출 setState 하면 사 순환 을 일 으 켜 프로그램 이 붕 괴 될 수 있다.
    6. 추천 사용법
    호출 setState 시 함수 전달 setState 값 을 사용 하여 리 셋 함수 에서 최신 업 데 이 트 된 state 값 을 가 져 옵 니 다.

    좋은 웹페이지 즐겨찾기