조건부 렌더링 

리액트에서 데이터를 받아오고 사용하는 과정에서

데이터를 받아와서 저장하는 속도보다 빠르게 컴포넌트가 렌더링 되는 경우가 시도때도 없이 가끔 있다.

이러한 경우 컴포넌트에서는 아직 저장되지 않은 데이터를 참조할 수 없기 때문에 컴포넌트에서는 에러를 뱉는다.🤮

 

 

에러에서는 빨간 글씨로'undefined 타입의 값에서 map이라는 이름의 속성을 읽을 수 없다' 라고 하고 있다.(맞나?🤔)

 

이러한 경우 컴포넌트가 렌더링 되는 시점에 조건을 주어, 조건에 맞지 않으면 렌더링을 막는 방법을 사용할 수 있는데,

이러한 방법을 조건부 렌더링이라고 한다.

조건부 렌더링을 이해하기 위해서는 리액트의 life cycle에 대해서 알고 있어야 한다. 


Life Cycle

 

life cycle이란 리액트의 컴포넌트가 생성되기부터 소멸될 때까지의 주기를 이야기한다.(생명주기)

리액트는 각 컴포넌트에서 이러한 생명주기 단계별로 메서드를 제공한다.

 

 

출처: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

 

위의 그림에서 보다시피 각 메서드들은 컴포넌트의 인스턴스가 생성되어 DOM객체 내에 삽입되는 과정에서 순서대로 호출된다.

 

생명주기 메서드가 불리는 순서

*진한 표시는 자주 사용되는 메서드

 

  1. constructor()
  2. static getDerivedStateFromProps()
  3. render()
  4. componentDidMount()
  5. static getDerivedStateFromProps()
  6. shouldComponentUpdate()
  7. render()
  8. getSnapshotBeforeUpdate() 
  9. componentDidUpdate()
  10. componentWillUnmount()

 

*componentWillMount(), componentWillUpdate(), componentWillReceiveProps()의 메서드는
비동기 렌더링 시 버그 발생의 가능성 때문에 더 이상 사용되지 않는다.

 

처음 컴포넌트가 불려지면 constructor() 메서드를 가장 먼저 거쳐서 바로 컴포넌트를 render() 하게 된다.

 

그 후에 컴포넌트가 마운트 된 직후(DOM 트리에 컴포넌트가 삽입된 직후)  componentDidMount() 메서드를 호출하게 되는데,

리액트 공식문서에서는 외부에서 데이터를 불러올 경우 네트워크 요청(fetch 등..)을 보내기 적절한 위치 라고 나와있다.

 

componentDidMount()에서 데이터를 불러온 후에는  setState()를 통해 불러온 데이터를 컴포넌트의 state에 저장하게 된다.

componentDidMount() {
  fetch(URL)
    .then((res) => res.json())
    .then((res) => this.setState({ monsters: res }));
}

 

setState()는 작업을 마친 후에 자동으로 render() 메서드를 실행시키게 되는데,

(state값 변경 시 setState()를 사용해야 하는 이유!)

 

render()를 통해 DOM 트리에 갱신된 컴포넌트를 삽입한 직후에 componentDidUpdate() 메서드가 호출된다.

 

componentDidUpdate()에서도 setState()를 통해 갱신된 데이터 적용이 가능하다.

하지만 이 경우 데이터가 갱신된 후에 다시 componentDidUpdate() 메서드를 호출하게 되기 때문에  무한 루프에 빠질 수 있다.

 

componentDidUpdate(setState()) => render() => componentDidUpdate(setState()) => ...

 

이러한 상황을 방지하기 위해 조건문으로 감싼 후에 로직을 작성하는 것이 필요하다.

componentDidUpdate(prevProps) {
  if (prevProps.match.params.id !== this.props.match.params.id) {
    fetch(URL)
      .then((res) => res.json())
      .then((result) =>
         this.setState({
             data: result,
           })
        );
  }
}

*componentDidUpdate() 메서드는 암묵적으로 가장 최근의 props와 state를 전달받는다.

 

 

이러한 순서들이 부모-자식 컴포넌트 간에서는 부모가 자식을 기다리는 것과 같이 일어나게 된다.
부모 컴포넌트에서 constructor()와 render()를 거쳐 자식 컴포넌트가 호출되게 되고,
자식 컴포넌트에서 constructor() => render() => componentDidMount()까지 호출된 후에
부모 컴포넌트의 componentDidMount()가 호출되게 된다.

 

life cycle console.log()

 

 


이러한 식으로 컴포넌트의 흐름을 알게 되었다면,

우리는 우리가 원하는 데이터가 있는 경우에만 렌더링을 시켜주는 방식으로 리액트 내에서의 에러를 방지할 수 있다.

render() {
        const { data } = this.state;
        return (
            this.state.data.name !== undefined && (
                <Card
                     key={data.id}
                     id={data.id}
                     name={data.name}
                     email={data.email}
                 />
            )
        );
    }

*조건문을 작성하는 방법은 몇 가지가 있지만 가독성이 좋은 단축 평가(&&연산자)를 선호한다.

'개발 > React' 카테고리의 다른 글

TIL #26 Context API & Redux (전역상태관리)  (0) 2021.05.17
TIL #19 (useState, useEffect)  (0) 2021.04.03
TIL #14 동적라우팅  (0) 2021.03.19
TIL #13 합성이벤트, key Prop  (0) 2021.03.10
TIL #11 state & props  (0) 2021.03.05

+ Recent posts