리액트에는 자체 메소드가 몇 가지 있다.
그 메소드 중 주로 쓰이는 것은 아래 다섯 가지다.
- constructor
- render
- componentDidMount
- componentDidUpdate
- componentWillUnmount
이 메소드들은 작성 순서와 관계 없이, 컴포넌트가 실행될 때 아래의 순서대로 작동한다.
constructor -> render -> componentDidMount -> (fetch 완료) -> render -> (setState) -> componentDidUpdate -> render -> componentWillUnmount
이런 흐름을 React Lifecycle이라고 한다.
render는 화면을 그리는 기능으로, 컴포넌트 안에서 꼭 필요한 메소드이다.
그러나, render는 JSX를 dom에 부착하는 역할을 가질 뿐이다.
그래서 화면에 dom이 모두 그려졌을 경우, 그 다음 순서인 componentDidMount을 실행한다.
그러나, componentDidMount는 render를 다시 불러주지 않는다.
따라서 setState 함수의 render 재실행 기능을 통해, 다시 한 번 화면을 출력해준다.
그리고, componentDidMount는 최초 render 시에만 실행되기 때문에
componentDidMount를 통해 재할당된 state를 기준으로 새로운 계산을 하고 싶은 경우에는,
componentDidUpdate를 통해 다음 계산을 진행한다.
더이상 사용하지 않는 컴포넌트를 제거하고 싶을 때는 componentWillUnmount를 통해 제거한다.
constuctor에 빈 배열이 있다고 가정하고,
componentDidMount에서 fetch와 setState가 일어나기 전에 render될 때는,
render 내에서 state의 정보를 console.log 해봐도 빈 배열만 출력될 뿐이다.
이 때, console.log(this.state.data[0]) 식으로 배열의 인덱스를 지정하면 빈 배열의 0 번 인덱스 정보를 지정한 것이기 때문에
그 유명한 TypeError: Cannot read property '0' of undefined
에러를 확인하게 된다....
조건부 렌더링을 통해 Error를 방지 할 수 있다.
예를 들어,
배열에 요소가 있는지 없는지를 length를 통해 확인해서 아래와 같이 조건부 렌더링을 구현할 수 있다.
console.log(this.state.data.length && this.state.data[0])
빈 배열일 경우, this.state.data.length
는 0일테니 false값을 가진다.
그러나 배열에 데이터가 있을 경우, this.state.data.length
는 값을 가질테니 true값을 가진다. 곧 AND 연산자 뒤의 코드가 실행된다.
(빈 배열도 true 값을 가지기 때문에, 꼭 배열의 length 값을 통해 조건을 걸어주자.)
부모 constructor -> 부모 render -> (부모 render 안에 있는 자식 컴포넌트로 이동한다) -> 자식 constructor -> 자식 render -> 자식 componentDidMount -> (부모 컴포넌트로 이동한다) -> 부모 componentDidMount -> (부모 fetch 및 setState 발생) -> render -> 부모 render (부모 render 안에 있는 자식 컴포넌트로 이동한다) -> 자식 render -> 자식 componentDidUpdate -> (부모 컴포넌트로 이동한다) -> 부모 componentDidUpdate
부모 컴포넌트에 아래와 같이 컴포넌트가 여러개 있을 경우,
<Child />
<Child />
<Child />
자식 컴포넌트 3개가 한 번에 constructor와 render를 반복한다.
자식 컴포넌트 3개 (자식 constructor, 자식 render)가 한 번에 일어나는 이유는,
react가 스스로 성능 유지를 하기 위해 commit 예약기능을 사용하기 때문이다.