React에는 컴포넌트가 로드될 때, 업데이트 될 때 , 제거될 때 자동으로 호출되는 API 가 존재
=> React 의 Life Cycle API
초기 생성 API
- 브라우저에 컴포넌트가 나타나기 전 호출되는 API
- Constructor(props) : 컴포넌트 생성자 함수로 컴포넌트가 새로 만들어 질 때마다 호출
- ComponentWillMount() : 컴포넌트가 화면에 나타나기 직전에 호출되는 API
-> 주로 서버를 호출하는 용도로 사용되었는데 이 API를 더 필요하지 않다고 판단하여 사용하지 않게 됨
기존에 처리하던 부분은 constructor이나 componentDidMount에서 처리할 수 있음 - componentDidMount() : 컴포넌트가 나타난 후 호출되는 api
주로 , D3, masonry 처럼 DOM 을 사용하는 외부 라이브러리와 연동하거나, 데이터를 axios , fetch 등 통해 ajax 요청하거나, DOM 속성(스크롤 설정, 크기 등) 읽거나 직접 변경하는 작업 함
업데이트 API
- 로드된 컴포넌트가 props, state 변화로 인해 변경될 경우 호출되는 API
- componentWillReceiveProps(nextProps) :
자식 component가 부모 Component로 부터 새로운 props 를 받게 되었을 때 호출
지금 사용되지 않음 -> getDerivedStateFromProps사용 - static getDerivedStateFromProps(nextProps, prevState) :
props로 받아온 값을 state로 동기화하는 작업을 해주어야할 경우 사용
setState로 state 값을 설정하는 것이 아니라, 객체 형태로 state 값 리턴
업데이트 할 값이 없다면 null 리턴 - shouldComponentUpdate(nextProps, nextState) :
- 컴포넌트 최적화 작업에서 유용
- React는 변화가 발생한 DOM 만 찾아 변화시켜주는데 자식 Component가 직접적으로
변하지 않았어도 부모가 Component가 리렌더링 되면 자식 컴포넌트도 다시 렌더링되며 Virtual Dom에 그려짐
- 기존 DOM 과 비교해서 변화가 발생했다면 실제 DOM 이 변경되고 그렇지 않다면 아무 작업도 하지 않음
- 많은 렌더링 작업 시 부하가 생기고 기본적으로 true 값을 반환하고 우리가 조작하는 상황에 따라 false를 반환
=> render() 함수를 호출하지 않게 되고, Virtual DOM 에 리렌더링하는 작업을 막을 수 있음 - componentWillUpdate(nextProps, nextState) :
- shouldComponentUpdate에서 true가 반환되었을 때 호출
- 주로 애니매이션 효과를 초기화하거나, 이벤트 리스너를 없애는 작업
- 이 함수 다음에 render 함수가 호출 (업데이트 이후 쓰이지 않음 아래 함수로 대체) - getSnapshotBeforeUpdate(prevProps, prevState) :
- render 함수와 ComponentDidUpdate사이에 호출
- DOM 변화가 일어나기 직전의 DOM 상태를 가져오고, 여기서 그 상태 값(snapshot)을 반환해서 ComponentDidUpdate의
3번째 매개변수로 받아 올 수 있다
- 예 ) 현재 위치 스크롤바 유지하고 싶을 때 => 이 , api에서 현재 스크롤바 위치를 구해 그 값을 매개 변수로 반환 => componentDidUpdate에서 그 인자를 받아 스크롤바 위치 처리를 해주는 식
- render -> getShapshotBeforeUpdate -> 실제 DOM 업데이트 -> componentDidUpdate 순 진행 - componentDidUpdate(prevProps, prevState, snapshot) :
- render 함수가 호출된 후 호출
- 이 시점에 this.props와 this.state는 변경된 상태
- 매개 변수로 받은 prevProps와 prevState는 말 그대로 변경 이전의 props와 state 값
제거 api
- 컴포넌트가 더 이상 사용되지 않을 경우 호출되는 api
- componentWillUnmount() :
- 주로 이벤트 제거 , setTimeout 의 clearTimeout 통한 제거 , setinterval의 clearInterval 통한 제거 작업
- 외부 라이브러리 사용 시, 이 부분에서 해당 라이브러리의 dispose 기능 실행
src/StopWatch.js 수정 (기존 코드 + 주석 범위 안에 life cycle api 추가)
// StopWatch.js
import React, { Component } from "react";
class StopWatch extends Component {
// class fields
state = {
sec: 0,
buttonFlag: true,
intervalFunction: null
};
/* Life Cycle API 영역 시작 */
constructor(props) {
super(props);
console.log("constructor");
}
componentWillMount() {
console.log("componentWillMount");
}
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
if (nextState.sec % 2 === 0) {
console.log(`${nextState.sec} % 2 === 0`);
return false;
}
return true;
}
componentWillUpdate(nextProps, nextState) {
console.log("componentWillUpdate");
}
componentDidUpdate(prevProps, prevState) {
console.log("componentDidUpdate");
}
/* Life Cycle API 영역 끝 */
// stop watch 로직
timer = () => {
// 비구조화 할당 사용( { sec } 부분 )
this.setState(({ sec }) => ({
sec: sec + 1
}));
};
// start 버튼 클릭
start = () => {
this.setState({
sec: 0,
buttonFlag: false,
intervalFunction: setInterval(this.timer, 1000)
});
};
// stop 버튼 클릭
stop = () => {
clearInterval(this.state.intervalFunction);
this.setState({
buttonFlag: true
});
};
render() {
return (
<React.Fragment>
<h1>Stop Watch</h1>
<div>
<b>{this.state.sec}</b>
<span>초</span>
{this.state.buttonFlag ? (
<button onClick={this.start}>start</button>
) : (
<button onClick={this.stop}>stop</button>
)}
</div>
</React.Fragment>
);
}
}
export default StopWatch;
=> state 내의 sec가 0,2,4 즉, 짝수일 경우 컴포넌트를 변화시키지 않으므로 shouldComponentUpdate 까지만 호출
실제 변화는 발생하지 않는다 / sec가 5 -> 6으로 변할 때 , stop 버튼을 눌러 state 안에 buttonFlag 값이 바뀌어
마지막에 컴포넌트의 변화가 일어남
에러 처리 api
-render 함수 안에 에러가 발생하면, react 앱 기능이 정지 / 이 현상을 막기 위해 사용하는 api
- componentDidCatch(error, info) :
- render 함수 안에 에러가 발생 시 , 호출
- state 에 error 이라는 변수를 생성 -> 해당 api 호출 시 setState로 error 값을 변경하여 render 함수 쪽에
다시 에러용 UI를 보여줌
- 주의점 : Component 자신의 render 함수에서 발생하는 에러는 잡을 수 없고, 자식 Component 내부의 render 함수에서 발생하는 에러를 잡음
src/StopWatch.js
-> ErrorMaker라는 인위적으로 에러를 생성하는 컴포넌트를 함수형 컴포넌트 형식으로 생성
StopWatch 내부에서는 state의 인자로 error라는 값이 추가되었고 Life Cycle API 영역에서 마지막에 componentDidCatch가 추가.
render 함수에서는 앱 중지를 방지하는 코드가 추가되었고, state의 sec가 3이 되었을 때, ErrorMaker를 생성.
// StopWatch.js
import React, { Component } from "react";
// Error 생성용 자식 Component
const ErrorMaker = () => {
throw new Error("Error Occured!!");
return <div />;
};
class StopWatch extends Component {
// class fields
state = {
sec: 0,
buttonFlag: true,
intervalFunction: null,
error: false
};
/* Life Cycle API 영역 시작 */
constructor(props) {
super(props);
console.log("constructor");
}
componentWillMount() {
console.log("componentWillMount");
}
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
if (nextState.sec % 2 === 0) {
console.log(`${nextState.sec} % 2 === 0`);
return false;
}
return true;
}
componentWillUpdate(nextProps, nextState) {
console.log("componentWillUpdate");
}
componentDidUpdate(prevProps, prevState) {
console.log("componentDidUpdate");
}
// render에서 에러 발생시 호출
componentDidCatch(error, info) {
console.log("componentDidCatch");
this.setState({
error: true
});
}
/* Life Cycle API 영역 끝 */
// stop watch 로직
timer = () => {
// 비구조화 할당 사용( { sec } 부분 )
this.setState(({ sec }) => ({
sec: sec + 1
}));
};
// start 버튼 클릭
start = () => {
this.setState({
sec: 0,
buttonFlag: false,
intervalFunction: setInterval(this.timer, 1000)
});
};
// stop 버튼 클릭
stop = () => {
clearInterval(this.state.intervalFunction);
this.setState({
buttonFlag: true
});
};
render() {
// 다음과 같이 에러 발생시에 앱이 중지되는 것을 방지한다.
if (this.state.error) return <h1>에러 발생!!</h1>;
return (
<React.Fragment>
<h1>Stop Watch</h1>
{this.state.sec === 3 && <ErrorMaker />}
<div>
<b>{this.state.sec}</b>
<span>초</span>
{this.state.buttonFlag ? (
<button onClick={this.start}>start</button>
) : (
<button onClick={this.stop}>stop</button>
)}
</div>
</React.Fragment>
);
}
}
export default StopWatch;
결과는?
x 버튼 누르면 다음과 같이 에러 발생 시 정해둔 ul 가 나타남
-> 에러 발생하는 이유는
- 존재하지 않는 함수를 호출하려고 할 때(props로 받은 줄 알았던 함수 호출할 때)
- 배열이나 객체를 받을 줄 알았을 때 존재하지 않을 때
render() {
// 에러를 유발시키는 유형(실제로 props에서 onClick(), object, array를 받지 않았다고 가정)
/*
this.props.onClick();
this.props.object.value;
this.props.array.length;
*/
// 다음과 같은 분기문을 추가하여 에러를 방지한다.
if( !this.props.object || this.props.array || this.props.array.length === 0 ) return null;
// ...
}
혹시 누락 발생할 거 같으면 다음과 같이 defaultProps를 이용해 해당 값들을 초기화해서 사용
class Test extends Component {
static defaultProps = {
onClick: () => console.warning("onClick is not defined"),
object: {},
array: []
}
}
인용
https://dev-kani.tistory.com/11?category=849014
'Language Study > React' 카테고리의 다른 글
3. props와 state (0) | 2019.11.26 |
---|---|
2. JSX (0) | 2019.11.26 |
1. 준비사항 (0) | 2019.11.26 |