React LifeCycle 2 - 업데이트 단계
리액트의 모든 컴포넌트는 생성-업데이트-제거라는 생명주기를 가지며, 각 생명주기에 실행되는 생명주기 메서드들을 가진다. 즉, 리액트 컴포넌트가 생성되고 제거될 때까지의 과정 안에서 특정 시점에 이에 매칭 되는 특정 메서드가 호출된다. 생명주기 메서드들은 개발자가 재정의하여 개발자가 원하는 시점에 원하는 로직이 실행되게 만들 수 있다. 이번 포스팅에선 업데이트 단계의 메서드에 대해 알아보자.
1. 생성 단계
2. 업데이트 단계 (now)
3. 제거 단계
1. getDerivedStateFromProps()
static getDerivedStateFromProps(nextProps, preState) {
if(nextProps.color !== preState.color) return { color: nextProps.color }
return null;
}
getDerivedStateFromProps()는 컴포넌트 최초 생성 시 constructor() 호출 직후, 혹은 props나 state가 변경되었을 때, forceUpdate()가 호출된 직후라는 3가지 경우에 호출되는(React v16.4 이상) 함수이다.
state가 props에 의존하는 경우. 특히, 현재 컴포넌트의 상태 값이 props의 이전 값과 이후 값 모두에게 의존해야 하는 경우에만 극히 드물게 구현되는 함수로, React LifeCycle 1 - 생성단계 포스팅의 설명을 참조하자.
2. shouldComponentUpdate()
shouldComponentUpdate(nextProps, nextState) {
return nextProps !== this.props || nextState !== this.state;
}
shouldComponentUpdate()는 render()를 호출하여 렌더링을 할지, 안 할지 결정하는 함수이다. state, props 변경, forceUpdate() 호출 시 getDerivedStateFromProps() 와 render() 사이에서 호출된다. 이 메서드에서 true를 리턴하면 render()가 호출되고, false를 리턴하면 render()가 호출되지 않으므로 렌더링 되지 않는다. shouldComponentUpdate()를 구현하지 않으면 항상 true를 리턴한다.
shouldComponentUpdate()를 얕은 수준의 비교로 구현한 리액트 컴포넌트를 PureComponent, 함수기반 컴포넌트에서 관련 최적화를 수행하기 위해 사용되는 요소를 React.memo라고 한다. 이들에 대해서는 https://study-ihl.tistory.com/216 에서 알아보자.
3. render()
render()는 컴포넌트가 화면에 그릴 요소들을 결정하는 메서드로, 컴포넌트 정의 시 반드시 작성해야 한다. render() 역시, 생성 단계 라이프 사이클 메서드에 포함되므로, React LifeCycle 1 - 생성 단계 포스팅의 설명을 참조하자.
4. getSnapshotBeforeUpdate()
getSnapshotBeforeUpdate(prevProps, prevState) {
// 이전 props와 현재 props의 길이를 비교 한다.
// 길이가 다른 경우,componentDidUpdate()에 이전 props의 list.length를 전달한다.
if (prevProps.list.length !== this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
getSnapshotBeforeUpdate()는 렌더링 된 결과가 DOM 등에 반영되기 직전에 호출된다. 이 메서드에서 반환된 값은 componentDidUpdate()의 인자로 전달된다. 예를 들어, 채팅 화면과 같이 스크롤 위치에 대한 처리가 필요할 때 DOM이 반영되기 직전인 getSnapshotBeforeUpdate()에서 스크롤 위치를 구하고, DOM이 반영된 직후인 componentDidUpdate()에서 스크롤 위치를 구하여, 이 둘을 비교한 후 DOM에 반영하는 것이다.
5. componentDidUpdate()
componentDidUpdate(prevProps, prevState, snapshot) {
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
componentDidUpdate()는 state가 갱신된 후, 렌더링된 결과가 DOM에 반영된 직후에 호출된다. 즉, componentDidUpdate()를 호출한 시점은 state 변화에 따른 DOM의 변화가 적용된 직후이므로, DOM을 조작하기 위해서, 혹은 이전과 현재의 props를 비교하여 네트워크 요청을 보내는 작업을 주로 수행한다(사용자 경험을 위해 브라우저에서 렌더링을 우선 수행한 후, 남은 시간에 네트워크 요청을 수행하는 개념). componentDidUpdate()는 최초 렌더링 시점에서는 호출되지 않는다.
컴포넌트에서 getSnapshotBeforeUpdate()를 구현하였다면, getSnapshotBeforeUpdate()의 리턴 값이 componentDidUpdate()의 세 번째 인자로 들어오므로, 이를 활용할 수 있다.
componentDidUpdate()에서 setState()를 호출할 경우 무한 렌더링이 발생할 수 있다. 리액트 컴포넌트는 state가 변하면 재 렌더링이 발생하는데, 렌더링 직후 componentDidUpdate() 호출에서 다시 setState()로 state를 변경하면 이 때문에 다시 재렌더링이 발생하는 과정이 무한으로 반복되는 것이다. 따라서 componentDidUpdate()에서 setState()를 사용할 때는 주의하여야 한다.
공식 문서 : https://ko.reactjs.org/docs/react-component.html#componentdidupdate