티스토리 뷰

blog.cloudboost.io/3-reasons-why-i-stopped-using-react-setstate-ab73fc67a42e

 

3 Reasons why I stopped using React.setState

Since a few months I’ve stopped using React’s setState on all my new React components. Don’t get me wrong, I didn’t stop having local…

blog.cloudboost.io

나는 리액트 컴포넌트의 local state 관리 방식을 사용하지 않기로 했다. 리액트 초심자에게 setState는 다소 까다롭다. 아래 상황을 보자.나는 리액트 컴포넌트의 local state 관리 방식을 사용하지 않기로 했습니다. 리액트 초심자에게 setState는 다소 까다롭습니다. 아래 상황을 봅시다.

리액트의 setState를 사용한 경우 이전에 클릭한 String이 console에 찍히는 미묘한 버그가 있습니다.


이런 버그가 생기는 원인은 리액트의 setState가 비동기로 동작하기 때문입니다.

첫번째이유. setState는 비동기로 동작합니다.

setState는 비동기로 동작하는것처럼 보이지 않기 때문에 위와 같이 가끔씩 미묘한 버그를 일으킵니다. 그리고 이런 버그는 왜 생기는지 찾기도 힘들죠.

 

위 예제의 JSX부분은 이렇게 생겼습니다.

render() {
    return (
      <ul onKeyDown={this.onKeyDown} tabIndex={0}>
        {this.props.values.map(value =>
          <li
            className={value === this.state.selection ? 'selected' : ''}
            key={value}
            onClick={() => this.onSelect(value)}
          >
            {value}
          </li> 
        )}  
      </ul>
    )
  }

각 li를 클릭하면 아래 onSelect함수가 실행되죠.

 

onSelect(value) {
  this.setState({ selection: value })
  this.fireOnSelect()
}

이때 매개변수로 전달된 value가 컴포넌트의 local state를 변경시킵니다.

그리고나서 fireOnSelect가 실행됩니다. 이때 이 fireOnSelect함수내에서 selection이라는 local state에 접근한다면 이 값은 업데이트가 된 selection일까요? 아니면 업데이트 되기 전의 selection일까요?

 

정답은 업데이트 되기 전의 selection입니다. 왜냐면, 위에서도 말했듯이, setState는 비동기로 동작하기 때문에 브라우저와 큐를 거쳐 다시 콜스택에 들어올때까지 실행되지 않습니다. (이 부분 이해가 안가시면 비동기 함수가 어떻게 동작하는지 검색해보시기 바랍니다.)

 

그래서 onSelect라는 함수가 전부 실행되기 전까지 컴포넌트의 local state인 selection은 업데이트 되지 않습니다. 그렇기 때문에 fireOnSelect에서 이전 state값을 참조하는거죠.

두번째이유. 불필요한 리렌더링을 유발합니다.

setState로 컴포넌트의 state를 변경하는경우 변경된 state가 렌더링과 관련이 되든 안되든 무조건 컴포넌트가 리렌더링 됩니다. 그래서 우리는 렌더링과 관련이 없는 state는 ref를 로컬 변수처럼 변형해서 사용하곤 했죠. 이것도 어떻게 보면 편법이었습니다.

 

그리고, setState를 통해 변경된 상태가 이전 상태와 똑같아서 리렌더링을 할 필요가 없음에도 리렌덩을 해야했습니다. 물론 shouldComponentUpdate에서 걸러주면 되긴 하지만, 매번 개발자가 이런 상황을 처리하는것 자체가 불편하고 코드를 길게 만듭니다.

 

세번째이유. setState는 모든 컴포넌트의 상태를 캡쳐하기엔 부족하다.

컴포넌트내에서 비동기 요청으로 서버에 api호출을 해보신적이 있으신가요? 이런 경우 컴포넌트가 마운트 된 다음 api를 호출하고 그 결과를 다시 setState로 반영해야 합니다. 사실 처음부터 데이터를 받아와서 한번만 렌더링 하면 됬었음에도 불구하고 리액트가 그렇게 동작하지 않기 때문에 불필요한 렌더링이 한번 더 일어나고 있는거죠. 그리고 생명주기 메소드들이 한번씩 더 재호출되죠.

만약에 setState로 컴포넌트가 렌더링 되기 전 상황을 캡쳐해서 실행시킬수 있었다면 이런 불필요한 과정들이 일어나지 않았겠죠?

 

Mobx로 컴포넌트의 local state를 관리하자.

우리는 local state는 리액트의 관리 방식을 채택하고 나머지 글로벌한 state만 Mobx로 적용해왔었습니다. 하지만 최근에는 local state까지도 mobx로 관리하기 시작했습니다.

@observer class Select extends React.Component {
  @observable selection = null; /* MobX로 local state 관리하기 */
  // ... 생략
 }

mobx의 상태 변화 메커니즘은 비동기로 동작하지 않기 때문에 버그가 없습니다.

 

setState가 비동기로 동작해서 발생하는 이슈도 Mobx에서는 발생하지 않습니다. Mobx의 상태 변화 메커니즘은 함수 호출 즉시 반영되며, setState와는 다르게 불변성 유지를 신경 쓰지 않아도 되서 컴포넌트의 코드가 매우 간결해집니다.

 

Mobx는 매우 똑똑해서 알아서 Observable이 렌더링과 관련 있는지 없는지 판단하고 필요한 경우에만 리렌더링합니다.

 

그래서 개발자가 아 이 속성은 렌더링과 관련없으니 ref로 관리해야지..

아 이 속성은 렌더링과 관련이 있으니 state로 관리하자..

와 같은 잡생각을 하지 않게 만들어줍니다.

 

또 하나의 엄청난 장점!

state가 컴포넌트 내부에 있든 글로벌하게 관리되는 store든 구분없이 신경쓰지말고 그냥 사용하면 됩니다.

Mobx는 당신의 컴포넌트를 작은 Store로 만들어줍니다.

특히나 리액트 초보자가 실수로 setState에 불변성을 유지 하지 않아서 리렌더링되지 않는 버그를 방지하는 효과를 줍니다. 리액트 초보자는들이 오히려 순수 리액트로 입문하는것보다 Mobx + React로 입문하는게 더 이해하기 쉬울거라고 생각합니다.

 

 

 

 

 

 

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함