React는 상태 업데이트 순서를 유지합니까?


140

React가 성능 최적화를 위해 비동기식으로 일괄 업데이트 상태를 수행 할 수 있음을 알고 있습니다. 따라서 호출 한 후에 업데이트 될 상태를 신뢰할 수 없습니다 setState. 하지만 당신은 반응 믿을 수 와 같은 순서로 상태를 업데이트 setState이라고 를 위해

  1. 같은 구성 요소?
  2. 다른 구성 요소?

다음 예제에서 버튼을 클릭하십시오.

1. 가능성 적이 있습니까 A가 거짓이고, b는 사실 에 대한이 :

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = { a: false, b: false };
  }

  render() {
    return <Button onClick={this.handleClick}/>
  }

  handleClick = () => {
    this.setState({ a: true });
    this.setState({ b: true });
  }
}

2. 가능성 적이 있습니까 A가 거짓이고, b는 사실 에 대한이 :

class SuperContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { a: false };
  }

  render() {
    return <Container setParentState={this.setState.bind(this)}/>
  }
}

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = { b: false };
  }

  render() {
    return <Button onClick={this.handleClick}/>
  }

  handleClick = () => {
    this.props.setParentState({ a: true });
    this.setState({ b: true });
  }
}

이것들은 나의 유스 케이스의 극단적 단순화입니다. 예 1의 첫 번째 상태 업데이트에 대한 콜백에서 두 번째 상태 업데이트를 수행하는 것뿐만 아니라 예제 1에서 동시에 두 상태 매개 변수를 업데이트하는 것과 같이 다르게 수행 할 수 있음을 알고 있습니다. 그러나 이것은 내 질문이 아닙니다. React가 이러한 상태 업데이트를 수행하는 잘 정의 된 방법이 있는지에 대해서만 관심이 있습니다.

문서로 뒷받침 된 답변은 대단히 감사합니다.


1
이것을보십시오 : stackoverflow.com/a/36087156/3776299
helb

3
그것은 무의미한 질문처럼 보이지 않습니다. 반응 페이지의 github 문제에 대해서도 질문 할 수 있습니다. dan abramov가 일반적으로 도움이됩니다. 그런 까다로운 질문을 받았을 때 물어보고 대답했습니다. 나쁜 점은 이러한 종류의 문제가 공식 문서와 같이 공개적으로 공유되지 않기 때문에 다른 사람들도 쉽게 액세스 할 수 있다는 것입니다. 또한 React 공식 문서에는 질문 등의 주제와 같은 일부 주제에 대한 광범위한 내용이 없다고 생각합니다.
giorgim

예를 들어 github.com/facebook/react/issues/11793을 살펴보십시오 .이 문제에서 논의 된 내용은 많은 개발자에게 도움이되지만 FB 직원은 고급 문서로 간주하기 때문에 공식 문서에는 해당되지 않습니다. 다른 것들도 마찬가지입니다. 나는 당신의 질문에서와 같이 상태 관리의 모든 구석 사건을 탐구하는 '심층적으로 반응하는 국가 관리'또는 '국가 관리의 함정'과 같은 제목의 공식 기사는 나쁘지 않을 것이라고 생각합니다. 아마도 우리는 FB 개발자에게 다음과 같은 것들로 문서를 확장하도록 강요 할 수 있습니다.)
giorgim

Thre는 내 질문에 매체에 대한 훌륭한 기사에 대한 링크입니다. 주 사용 사례의 95 %를 포괄해야합니다. :)
Michal

2
@Michal 그러나 그 기사는 여전히이 질문에 대답하지 않습니다 IMHO
giorgim

답변:


336

나는 React에서 일합니다.

TLDR :

그러나 setState가 호출 된 것과 동일한 순서로 React를 신뢰하여 상태를 업데이트 할 수 있습니까?

  • 같은 구성 요소?

예.

  • 다른 구성 요소?

예.

위해 업데이트는 항상 존중한다. 중간 상태가 "사이에"있는지 여부는 배치 내부에 있는지 여부에 따라 다릅니다.

현재 (React 16 이하) React 이벤트 핸들러 내부의 업데이트 만 기본적으로 배치됩니다 . 드문 경우에 필요한 경우 이벤트 핸들러 외부에서 일괄 처리를 강제 실행하는 불안정한 API가 있습니다.

이후 버전 (아마도 React 17 이상)에서는 React가 기본적으로 모든 업데이트를 일괄 처리하므로 이에 대해 생각할 필요가 없습니다. 항상 그렇듯이 React 블로그 와 릴리스 노트에서 이에 대한 변경 사항을 발표 할 것 입니다.


이것을 이해하는 열쇠 는 React 이벤트 핸들러 내 에서 얼마나 많은 컴포넌트를 호출 하는지에 관계없이 이벤트 가 끝날 때 단일 재 렌더링을 생성한다는 것setState() 입니다. 경우 때문에 많은 분야에서 좋은 성능을 위해 매우 중요 Child하고 Parent각 호출 setState()클릭 이벤트를 처리, 당신은 다시 렌더링하지 않으려 Child두 번.

두 예제 모두 setState()에서 React 이벤트 핸들러 내 에서 호출이 발생합니다. 따라서 이벤트가 끝날 때 항상 함께 플러시됩니다 (중간 상태가 표시되지 않음).

업데이트는 항상 발생 순서대로 얕게 병합됩니다 . 제 업데이트되도록하는 경우 {a: 10}, 제이며 {b: 20}, 세 번째는 {a: 30}, 렌더링 된 상태가 될 것이다 {a: 30, b: 20}. 동일한 상태 키에 대한 최신 업데이트 (예 : 예와 같이 a)는 항상 "승리"됩니다.

this.state목적은 우리가 때 일괄 처리의 끝에서 UI를 다시 렌더링 업데이트됩니다. 따라서 이전 상태 (예 : 카운터 증가)를 기반으로 상태를 업데이트해야하는 경우을 setState(fn)읽는 대신 이전 상태를 제공 하는 기능 버전을 사용해야합니다 this.state. 이에 대한 추론이 궁금하다면 이 주석 에서 자세히 설명했습니다 .


귀하의 예에서, 일괄 처리가 활성화 된 React 이벤트 핸들러 내부에 있기 때문에 "중간 상태"가 표시되지 않습니다 (이벤트가 종료 될 때 React가 "알고 있기 때문에").

그러나 React 16 및 이전 버전 모두에서 기본적으로 React 이벤트 핸들러 외부에는 일괄 처리가 없습니다 . 따라서 귀하의 예에서 AJAX 응답 핸들러 대신 AJAX 응답 핸들러가있는 handleClick경우 각각 setState()발생하는 즉시 처리됩니다. 이 경우 예, 중간 상태 표시됩니다.

promise.then(() => {
  // We're not in an event handler, so these are flushed separately.
  this.setState({a: true}); // Re-renders with {a: true, b: false }
  this.setState({b: true}); // Re-renders with {a: true, b: true }
  this.props.setParentState(); // Re-renders the parent
});

이벤트 핸들러에 있는지 여부에 따라 동작이 달라지는 것이 불편하다는 것을 알고 있습니다 . 향후 React 버전에서는 기본적으로 모든 업데이트를 일괄 처리하고 변경 사항을 동 기적으로 플러시하는 옵트 인 API를 제공하는 React 버전에서 변경 될 것입니다. 기본 동작 (잠재적으로 React 17에서)을 전환 할 때까지 일괄 처리를 수행하는 데 사용할 수있는 API가 있습니다 .

promise.then(() => {
  // Forces batching
  ReactDOM.unstable_batchedUpdates(() => {
    this.setState({a: true}); // Doesn't re-render yet
    this.setState({b: true}); // Doesn't re-render yet
    this.props.setParentState(); // Doesn't re-render yet
  });
  // When we exit unstable_batchedUpdates, re-renders once
});

내부적으로 React 이벤트 핸들러는 모두 래핑 unstable_batchedUpdates되므로 기본적으로 일괄 처리됩니다. 업데이트를 unstable_batchedUpdates두 번 래핑해도 효과가 없습니다. 가장 바깥 쪽 unstable_batchedUpdates통화를 종료하면 업데이트가 플러시됩니다 .

일괄 처리가 기본적으로 활성화되어 있으면 API를 제거한다는 의미에서 해당 API는 "불안정"합니다. 그러나 부 버전에서는 제거하지 않으므로 React 이벤트 핸들러 외부에서 강제로 일괄 처리를 수행해야하는 경우 React 17까지 안전하게 사용할 수 있습니다.


요약하면 React는 기본적으로 이벤트 처리기 내부의 배치 만 처리하기 때문에 혼란스러운 주제입니다. 이것은 향후 버전에서 변경 될 것이며, 그 동작은 더 간단 할 것입니다. 그러나 솔루션은 배치를 적게하는 것이 아니라 기본적 으로 배치늘리는 것 입니다. 그것이 우리가 할 일입니다.


1
"항상 주문을 올바르게 얻는" 방법 중 하나 는 임시 객체를 생성하고 다른 값 (예 :)을 할당 obj.a = true; obj.b = true한 다음 마지막에하는 것 this.setState(obj)입니다. 이벤트 핸들러 내부에 있는지 여부에 관계없이 안전합니다. 이벤트 핸들러 외부에서 상태를 여러 번 설정하는 실수를 자주 저지르면 깔끔한 트릭이 될 수 있습니다.
Chris

따라서 우리는 실제로 일괄 처리를 하나의 이벤트 핸들러로만 제한 할 수는 없습니다. 그런 다음 가장 최신 상태에 액세스하기 위해 업데이터 함수와 함께 setState를 사용해야합니다. 그러나 XHR에서 일부 데이터를 읽은 다음 상태로 만들려면 state.filter를 사용해야하는 경우 어떻게해야합니까? 콜백이 지연된 XHR (및 부작용)이있는 업데이터에 XHR을 넣어야 할 것 같습니다. 그렇다면 모범 사례로 간주됩니까?
Maksim Gumerov

1
그리고 그것은 또한 우리가 this.state에서 전혀 읽지 말아야한다는 것을 의미합니다. state.X를 읽는 유일한 합리적인 방법은 인수에서 updater 함수로 읽는 것입니다. 그리고 this.state에 쓰는 것도 안전하지 않습니다. 그렇다면 왜 this.state에 대한 액세스를 허용합니까? 그것들은 아마도 독립형 질문을해야하지만 대부분 설명이 맞다면 이해하려고합니다.
막심 Gumerov

10
이 답변은 reactjs.org 문서에 추가해야합니다
Deen John

2
componentDidUpdateReact 16에서 "React event handler"에 기타 라이프 사이클 콜백이 포함되어 있다면이 게시물 에서 설명해 주시겠습니까? 미리 감사드립니다!
Ivan

6

이것은 실제로 매우 흥미로운 질문이지만 대답은 너무 복잡해서는 안됩니다. 대답이있는 매체 에는이 훌륭한 기사 가 있습니다 .

1) 이렇게하면

this.setState({ a: true });
this.setState({ b: true });

나는 상황이있을 것이라고 생각하지 않는 atrue하고 b있을 것입니다 false때문에 일괄 처리 .

그러나 경우 b에 따라 달라집니다a 다음이 실제로 당신이 예상되는 상태를 얻을 수 없겠죠 상황 일 수 있습니다.

// assuming this.state = { value: 0 };
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});

위의 모든 통화가 처리 this.state.value되면 예상대로 3이 아닌 1 이됩니다 .

이것은 기사에서 언급되었습니다. setState accepts a function as its parameter

// assuming this.state = { value: 0 };
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));

이것은 우리에게 줄 것이다 this.state.value === 3


어떤 경우 this.state.value(이벤트 핸들러에서 모두 업데이트됩니다 setState(여기서 일괄 처리한다) 및 AJAX 콜백 setState되어 있지 일괄 처리). 이벤트 핸들러에서는를 사용하여 updater function항상 함수가 제공하는 현재 업데이트 된 상태를 사용하여 상태를 업데이트합니다. setState일괄 처리되지 않았다는 것을 알고 있어도 AJAX 콜백의 코드 내에서 업데이터 함수와 함께 사용해야합니까 ? setState업데이터 기능을 사용하거나 사용하지 않고 AJAX 콜백 내에서 의 사용법을 명확히 설명해 주시겠습니까? 감사합니다!
tonix

@Michal, 안녕 Michal은 단지 단순한 질문을하고 싶었습니다. 이것이 우리에게 this.setState ({value : 0}); this.setState ({value : this.state.value + 1}); 첫 번째 setState는 무시되고 두 번째 setState 만 실행됩니까?
Dickens

@Dickens 나는 둘 다 setState처형 될 것이라고 생각 하지만 마지막 것은 이길 것입니다.
Michal

3

동일한주기 동안 여러 통화가 함께 일괄 처리 될 수 있습니다. 예를 들어, 동일한주기에서 품목 수량을 두 번 이상 늘리려 고하면 다음과 같은 결과가 발생합니다.

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

https://reactjs.org/docs/react-component.html


3

의사 처럼

setState ()는 변경 사항을 구성 요소 상태로 대기열에 넣고이 구성 요소와 해당 하위 항목을 업데이트 된 상태로 다시 렌더링해야 함을 React에 알립니다. 이벤트 핸들러 및 서버 응답에 대한 응답으로 사용자 인터페이스를 업데이트하는 데 사용하는 기본 방법입니다.

대기열에서와 같이 변경을 수행합니다 ( FIFO : First In First Out) 첫 번째 호출이 먼저 수행됩니다


안녕 알리, 그냥 질문을하고 싶었다, 우리가 this.setState ({value : 0}); this.setState ({value : this.state.value + 1}); 첫 번째 setState는 무시되고 두 번째 setState 만 실행됩니까?
Dickens
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.