일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 세부
- 중고나라
- Webpack
- 특수문자
- 사기
- 여행
- 자동완성
- webpack.config.js
- 마사지
- 정직하게사세요
- JavaScript
- Hooks
- autocomplate
- 막탄
- 해외여행
- js
- 스노쿨링
- plugin
- ES6
- 중고나라사기
- 구분
- 유효성검사
- 삼성무선청소기제트
- 정규식
- REACT
- 맛사지
- 네이버페이사기
- 스쿠버다이빙
- 중고거래사기
- Today
- Total
Ryu.log
[ React-Tutorial-05 ] LifeCycle API 본문
LifeCycle API에 대해서 알아보자. 이 API는 Component가 브라우저에서 나타날 때, 사라질 때, 그리고 업데이트 될 때 호출되는 API 이다.
정말 중요한 역할을 하니깐! 세세히 파헤쳐야 한다!
컴포넌트가 브라우저에 나타나기 전 호출되는 API
- constructor
- componentWillMount ---> React v16.3 부터 사용되지 않음
- componentDidMount
컴포넌트가 업데이트 될 때 ( props,state 변화가 있을 때 ) 호출되는 API
- componentWillReceiveProps ---> React v16.3 부터 사용되지 않음
- [NEW] static getDerivedStateFromProps()
- shouldComponentUpdate
ComponentWillUpdate ---> React v16.3 부터 사용되지 않음
[NEW] getSnapshotBeforeUpdate()
componentDidUpdate
01. Component 초기 생성
constructor(props) { super(props); }
이전에 언급한 적이 있듯이, 이부분은 Component 생성자 함수이다. Component가 새로 만들어질때마다 이함수가 호출된다.
componentWillMount ---> React v16.3 부터 사용되지 않음
componentWillMount() { }
이 API는 Component가 화면에 나가기 직전에 호출되는 API 인데, 이 API에 대해서는 별로 신경을 쓰지 않아도 된다.
원래는 주로 브라우저가 아닌 환경에서( 서버사이드 )도 호출하는 용도로 사용했었는데,
이 API가 더이상 필요하지 않게 되어 React v16.3에서는 해당 API가
deprecated 되었으니, 아~ 옛날엔 이런 API가 사용됬었구나.. 정도만 알아두면된다.
v16.3 이후부터는 UNSAFE_componentWillMount() 라는 이름으로 사용된다.
기존에 이 API에서 하던 것들은 위에있는 construct와 아래에서 다뤄볼 componentDidMount에서 충분히 처리할 수 있다.
componentDidMount
componentDidMount() { // 외부 라이브러리 연동: D3, masonry, etc // 컴포넌트에서 필요한 데이터 요청: Ajax, GraphQL, etc // DOM 에 관련된 작업: 스크롤 설정, 크기 읽어오기 등 }
이 API는 컴포넌트가 화면에 나타나게 되었을 때 호출된다. 여기선 주로 D3, masonry 처럼 DOM을 사용해야하는 외부 라이브러리 연동을 하거나,
해당 Component에서 필요하는 데이터를 요청하기위해 axios, fetch 등을 통하여 ajax요청을 하거나,
DOM의 속성을 읽거나 직접 변경하는 작업을 진행한다.
02. Component 업데이트
componentWillReceiveProps(nextProps) { // this.props 는 아직 바뀌지 않은 상태 }
이 API는 Component가 새로운 props를 받게 되었을 때 호출된다. 이 안에서는 주로, state가 props에 따라 변해야 하는 로직을 작성한다.
새로 받게될 props는 nextProps로 조회할 수 있으며, 이 때 this.props를 조회하면 업데이트 되기 전의 API이니 참고바란다.
이 API 또한 v16.3부터 deprecate 된다. v16.3부터는 UNSAFE_componentWillReceiveProps() 라는 이름으로 사용된다.
그리고 이 기능은 상황에 따라 새로운 API getDerivedStateFromProps 로 대체 될 수도 있다.
[NEW] static getDerivedStateFromProps()
static getDerivedStateFromProps(nextProps, prevState) { // 여기서는 setState 를 하는 것이 아니라 // 특정 props 가 바뀔 때 설정하고 설정하고 싶은 state 값을 리턴하는 형태로 // 사용됩니다. /* if (nextProps.value !== prevState.value) { return { value: nextProps.value }; } return null; // null 을 리턴하면 따로 업데이트 할 것은 없다라는 의미 */ }
이 함수는, v16.3 이후에 만들어진 라이프사이클 API 이다, 이 API는 props로 받아온 값을 state로 동기화 하는 작업을 해줘야 하는 경우에 사용된다.
shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState) { // return false 하면 업데이트를 안함 // return this.props.checked !== nextProps.checked return true; }
이 API는 Component를 최적화 하는 작업에서 매우 유용하게 사용된다.
이전 글에서 "React에서는 변화가 발생하는 부분만 업데이트를 해줘서 성능이 꽤 잘나온다" 라고 언급한 적이 있다.
하지만, 변화가 발생한 부분만 감지해내기 위해서는 Virtual DOM에 한번 그려줘야 한다.
즉, 현재 Component의 상태가 업데이트되지 않아도, 부모 Compnent가 리렌더링되면, 자식 Component들도 랜더링이 된다. 여기서 "랜더링" 된다는 건,
render() 함수가 호출된다는 의미이다.
변화가 없으면 물론 DOM조작은 하지 않게 된다.
다만 Virutal DOM에 리랜더링 하는것도, 불필요할 경우에는 방지 차원에서 shouldComponentUpdate를 작성하면 된다.
이 함수는 기본적으로 ture를 반환한다. 따로 작성을 해주어서 조건에 따라 false를 반환하면 해당조건에는 render 함수를 호출하지 않는다.
ComponentWillUpdate ---> React v16.3 부터 사용되지 않음
componentWillUpdate(nextProps, nextState) { }
이 API는 shouldComponentUpdate에서 true를 반환했을때만 호출된다. 만약에 false를 반환했었다면 이 함수는 호출되지 않는다.
여기선 주로 애니메이션 효과를 초기화하거나, 이벤트 리스너를 없애는 작업을 한다.
이 함수가 호출되고 난 다음에는, render()가 호출된다. 이 API 또한 v16.3 이후 deprecate 된다.
기존 기능은 getSnapshotBeforeUpdate 로 대체 가능하다.
[NEW] getSnapshotBeforeUpdate()
이 API가 발생하는 시점은 다음과 같다.
- render()
- getsnapshotBeforUpdate()
- 실제 DOM에 변화 발생
- componentDidUpdate
이 API를 통해서, DOM 변화가 일어나기 직전의 DOM 상태를 가져오고, 여기서 리턴하는 값은 componentDidUpdate에서 3번째 파라미터로 받아올 수 있게 된다.
getSnapshotBeforeUpdate(prevProps, prevState) { // DOM 업데이트가 일어나기 직전의 시점입니다. // 새 데이터가 상단에 추가되어도 스크롤바를 유지해보겠습니다. // scrollHeight 는 전 후를 비교해서 스크롤 위치를 설정하기 위함이고, // scrollTop 은, 이 기능이 크롬에 이미 구현이 되어있는데, // 이미 구현이 되어있다면 처리하지 않도록 하기 위함입니다. if (prevState.array !== this.state.array) { const { scrollTop, scrollHeight } = this.list; // 여기서 반환 하는 값은 componentDidMount 에서 snapshot 값으로 받아올 수 있습니다. return { scrollTop, scrollHeight }; } } componentDidUpdate(prevProps, prevState, snapshot) { if (snapshot) { const { scrollTop } = this.list; if (scrollTop !== snapshot.scrollTop) return; // 기능이 이미 구현되어있다면 처리하지 않습니다. const diff = this.list.scrollHeight - snapshot.scrollHeight; this.list.scrollTop += diff; } }
전체 코드는 https://codesandbox.io/s/484zvr87ow 에서 확인 가능하다.
componentDidUpdate
componentDidUpdate(prevProps, prevState, snapshot) { }
이 API는 Component에서 render()를 호출하고 난 다음에 발생하게 된다. 이 시접에선 this.props 와 this.state가 바뀌어 있다.
그리고 파라미터를 통해 이전의 값인 prevProps와 prevState를 조회할 수 있다. 그리고, getSnapshotBeforeUpdate 에서 반환한 snpshot 값은 세번째 값으로 받아온다.
03. Component 제거
componentWillUnmount() { // 이벤트, setTimeout, 외부 라이브러리 인스턴스 제거 }
여기서는 주로 등록했던 이벤트를 제거하고, 만약에 setTimeout을 걸은 것이 있다면 clearTimeout을 통하여 제거를 한다. 추가적으로,
외부 라이브러리를 사용한게 있고 해당 라이브러리에 dispose 기능이 있다면 여기서 호출해주면 된다.
04. 직접 사용해보기
import React, { Component } from 'react'; class Counter extends Component { state = { number: 0 } constructor(props) { super(props); console.log('constructor'); } componentWillMount() { console.log('componentWillMount (deprecated)'); } componentDidMount() { console.log('componentDidMount'); } shouldComponentUpdate(nextProps, nextState) { // 5 의 배수라면 리렌더링 하지 않음 console.log('shouldComponentUpdate'); if (nextState.number % 5 === 0) return false; return true; } componentWillUpdate(nextProps, nextState) { console.log('componentWillUpdate'); } componentDidUpdate(prevProps, prevState) { console.log('componentDidUpdate'); } handleIncrease = () => { const { number } = this.state; this.setState({ number: number + 1 }); } handleDecrease = () => { this.setState( ({ number }) => ({ number: number - 1 }) ); } render() { console.log('render'); return ( <div> <h1>카운터</h1> <div>값: {this.state.number}</div> <button onclick="{this.handleIncrease}">+</button> <button onclick="{this.handleDecrease}">-</button> </div> ); } } export default Counter;
콘솔에 찍히는 메시지들을 확인해보고, 5의 배수일때 Component가 리랜더링 되지 않는 것도 확인해보자.
05. Component 애러 발생
componentDidCatch(error, info) { this.setState({ error: true }); }
에러가 발생하면 이런식으로 componentDidCatch가 실행되게 하고, state.error 를 true로 설정하게하고,
render 함수 쪽에서 이에 따라 에러를 띄워주면 된다.
이 API를 사용하게 될 때는 주의할 점이 있는데, Component 자신의 render 함수에서 에러가 발생해버리는 것은 잡아낼 수는 없지만,
그 대신에 Component의 자식 Component 내부에서 발생하는 에러들을 잡아낼 수 있다.
일단, 문제가 발생하는 코드를 작성해 보면
import React, { Component } from 'react'; const Problematic = () => { throw (new Error('버그가 나타났다!')); return ( <div> </div> ); }; class Counter extends Component { // ... 생략 render() { return ( <div> <h1>카운터</h1> <div>값: {this.state.number}</div> { this.state.number === 4 && <Problematic /> } <button onClick={this.handleIncrease}>+</button> <button onClick={this.handleDecrease}>-</button> </div> ); } } export default Counter;
Problematic 이라는 Comonent를 만들고 이 값이 4가 되면 렌더링을 하도록 설정했다.
Problematic 은 렌더링이 될 때 에러가 발생했음을 알리는 throw를 사용하게끔 했는데, 한번 카운터 값이 4까지 올라갔을때 어떻게 되는지 확인해보자.
여기서 이렇게 빨갛게 Error: 버그가 나타났다! 라고 뜨는 부분은 개발모드에서 제공해주는 기능이다. 프로덕션에서는 이 화면이 나타나지 않는다.
여기서 x를 눌러보면
그냥 비어있는 페이지가 나타난다.
자, 그럼 componentDidCatch를 통해 자식 Component에서 발생한 에러를 잡아보자.
import React, { Component } from 'react'; const Promblematic = () => { throw (new Error('버그가 나타났다!')); return ( <div> </div> ); }; class Counter extends Component { state = { number: 0, error: false } // (...) componentDidCatch(error, info) { this.setState({ error: true }); } render() { if (this.state.error) return (<h1>에러발생!</h1>); return ( <div> <h1>카운터</h1> <div>값: {this.state.number}</div> { this.state.number === 4 && <Promblematic /> } <button onClick={this.handleIncrease}>+</button> <button onClick={this.handleDecrease}>-</button> </div> ); } } export default Counter;
이제 어떻게 되나 보면,
다시 카운터를 4로 올리면, 빨간 에러창은 여전히 뜬다, 하지만 X를 눌렀을 때, 앱이 크래쉬되는 것이 아니라 에러가 발생했다는 메시지가 뜨게 된다.
보통, 렌더링 부분에서 오류가 발생하는 것은 사전에 방지해 주어야 한다. 주로 자주 에러가 발생하는 이유는 아래와 같다.
존재하지 않는 함수를 호출하려고 할 때 (예를 들어서 props로 받을줄 알았던 함수가 전달되지 않았을 때)
this.props.onClick();
배열이나 객체가 올 줄 알았는데, 해당 객체나 배열이 존재하지 않을 때
this.props.object.value; // object is undefined this.props.array.length; // array is undefined
이러한 것들은 render 함수에서 다음과 같은 형식으로 막아 줄 수 있다.
render() { if (!this.props.object || !this.props.array || this.props.array.length ===0) return null; // object 나 array 를 사용하는 코드 }
혹은, 이전에 언급한 Component의 기본값을 설정하는 defaultProps를 통해서 설정하면 된다.
class Sample extends Component { static defaultProps = { onIncrement: () => console.warn('onIncrement is not defined'), object: {}, array: [] } }
이걸로도 놓쳐버린 버그들은 componentDidCatch를 통해서 잡아주고, 필요시엔 에러의 세부내용을 서버쪽에 기록하게 해서 조사해보면 될 것이다.
React Component가 사용 될 때 각 상황에 따라 호출되는 LifeCycle API 들에 대해서 알아 보았는데. 이 API들은 알아두면 여러상황에 유용하게 쓸 수 있으니,
어떠한 API 들이 있는지 인지해 두고, 나중에 해결해야 할 문제가 발생했을 때 사용하면 될 것이다.
이 글은 Velopert님의 블로그에서 React 포스팅을보며 실습하며 공부한 자료입니다.
'Prev-content' 카테고리의 다른 글
[ React-Tutorial-07 ] 배열다루기1 생성과 랜더링 (0) | 2018.05.30 |
---|---|
[ React-Tutorial-06 ] input 상태관리 (0) | 2018.05.30 |
[ React-Tutorial-04 ] props와 state (0) | 2018.05.25 |
[ React-Tutorial-03 ] JSX (0) | 2018.05.25 |
[ React-Tutorial-02 ] React 프로젝트 시작 (0) | 2018.05.24 |