componentDidMount에서 setState를 쓰지 말아야 하는 이유

페이스북 다른 그룹에 올라온 질문인데 답변이 길어질 것 같아 여기에 정리해 봅니다.

“componentDidMount에서 setState를 쓰지 말아야 하는 이유” 제목을 더 정확히 풀어쓰면 “componentDidMount 함수가 호출되고 나서 종료(return)되는 사이에 setState가 호출되는 것은 바람직하지 않다” 입니다.

이 바람직하지 않은 대표적인 코드가 다음과 같습니다.

componentDidMount(){
  this.setState({
    max: this.props.numbers
  });
)

componentDidMount가 호출되는 시점은 tree에 붙은 다음이니 이미 렌더링 되어 화면에 보인 직후입니다.
그런데 여기서 setState를 하게 되면 바뀐 state를 기준으로 다시 렌더링 됩니다.

여기서 주목해야 할 점은 두 번이나 렌더링되었다는 점입니다.
여기서 이 max라는 state에 대해 다음 질문을 던져봅시다.

이 state는 mount되기 전에 값을 지정할 수 있는 없는 값인가?

아니요! props를 가지고 계산하는 값이므로 constructor에서 지정하면 mount되기 전에 결정되어 렌더링을 한번만 하게 됩니다.

// 개선 후
constructor(props){
  this.state = {
     max: this.props.numbers
  }
}
componentDidMount(){
}

componentDidMount 코드 안에 setState를 쓴 예제를 많이 봤는데요??

componentDidMount(){
  fetch('http://my.api/whatever').then((data)=>{
    this.setState({ data });
  });
}

props가 아닌 비동기적으로 값을 가져온 다음에 그 값을 가지고 state를 바꿔야 하는 예제를 보신 겁니다. mount 되기 전에 결정할 수 없는 값입니다. 예를 들어서 웹페이지에서 1) 글 목록으로 가기를 클릭해서 2) 글목록이 화면에 마운트 되고 3) 마운트 직후 새로운 데이터를 가져와서 state를 바꾸는 경우입니다. 이건 mount되기 전에 결정할 수 있는 값이 아닙니다. 따라서 이 경우는 전혀 문제가 되지 않습니다. setState의 실행 시점을 보면 setState는 componentDidMount가 종료(return)된 다음입니다.

다시 한번 정리해보면 위에서 말한 것처럼 componentDidMount 함수가 호출되고 나서 종료(return)되는 사이에 setState가 호출되는 것은 바람직하지 않습니다.

위 설명은 나쁜 practice이니 "이 것 하지 마"라는 설명인데 사실 Best practice 관점에서 설명해보면 다음과 같습니다.

렌더링 되기 전에 이미 결정되는 state는 rendering 되기 전인 static stateconstructor에서 값을 지정해놓자

2개의 좋아요
componentDidMount(){
  this.setState({
    max: this.props.numbers
  });
)

그저 이렇게 쓴다는게 놀랍네요 ;;; 이런 예제코드를 본적도 없는데… ㅎㄷ~