티스토리 뷰

리렌더: 이미 스크린에 그려진 컴포넌트가 다시 렌더링되는 것

리렌더링은 언제 일어나는가?

  1. 컴포넌트의 state이 변했을 때, 해당 컴포넌트가 리렌더링됨
  2. 부모가 리렌더링됐을 때, 자식 컴포넌트도 리렌더링됨 (엣지 케이스가 있으나 일반적으로 그러함)
  3. Context Provider 값이 바뀌었을 때 그 컨텍스트를 사용하는 모든 컴포넌트가 리렌더링됨.
  4. 훅 내부에서 state 변동이 있거나 context 값 변동이 있으면 그 훅을 소비하는 컴포넌트가 리렌더링 됨.
function Parent() {
    const [state, setState] = useState(); // state이 변하면 Parent 컴포넌트 리렌더링됨
    // Parent 컴포넌트가 리렌더링되면 Child 컴포넌트도 리렌더링됨
    return <Child /> 
}

반대로 리렌더링을 유발하지 않는 것은?

  • props 변동 (메모이제이션된 컴포넌트는 예외임)
    • props가 변동하기 위해서는 부모 컴포넌트의 값이 업데이트되어야 하고 이것이 리렌더링을 유발하고 부모 컴포넌트가 리렌더링되면 자식 컴포넌트도 리렌더링되지만 이것은 props 변동이 원인인 것이 아님.
    • 값의 업데이트가 일어나면서 리렌더링과 props변동 둘 다 일어나는 것뿐.

컴포넌트 합성으로 리렌더링 방지하기

안티패턴

  • 컴포넌트 안에서 새로운 컴포넌트를 생성해서 리턴하기
    function Component() {
      // 컴포넌트 안에서 컴포넌트 생성 후 리턴
      function SlowComponent() {
          return <Something />
      }
      return <SlowComponent />
    }
    위 케이스는 Component가 리렌더링 될 때마다 SlowComponent가 re-mount되기 때문에 단순한 리렌더링보다 더 느리다. 하지만 굳이 이렇게 코딩하는 경우는 크게 없을 듯.

state를 밑으로 내리기

  • state가 변동하면 해당 컴포넌트가 리렌더링되고 모든 Child가 또 리렌더링되기 때문에, state를 국한지을 수 있다면 가급적 부모보다는 낮은 위계에 분리시키는 것이 불필요한 리렌더링을 줄일 수 있다.
// 이 경우 state가 변경되면 children인 VerySlowComponent도 리렌더링된다. 
function Component {
    const [open, setOpen] = useState(false);
    return (
        <Container>
            <Button onClick={() => {setOpen(true)}} />
            {isOpen && <ModalDialog />}
            <VerySlowComponent />
        </Container>
    )
}
// children의 리렌더링은 parent에게 영향을 주지 않으므로, 이 케이스에서는 VerySlowComponent가 영향받지 않는다. 
function ModalOpenButton() {
    const [open, setOpen] = useState(false);

    return (
        <>
            <Button onClick={() => {setOpen(true)}} />
            {isOpen && <ModalDialog />}
        </>
    )
}

function Component {
    return (
        <Container>
            <ModalOpenButton />
            <VerySlowComponent />
        </Container>
    )
}

Children as prop

  • 이것도 state를 내리는 기법과 비슷하다. state를 보다 작은 부분에 캡슐화해서 분리하는 방법이기 때문이다.
  • 그리고 props가 리렌더링이랑 관련없다는 사실도 기억해야 한다.
function Component() {
    const [state, setState] = useState()

    return (
        <div onScroll={(e) => setState(e)}>
            <VerySlowComponent />
        </div>
    )
}
function Scroll({ children }) {
    const [state, setState] = useState()
    return (
        <div onScroll={(e) => setState(e)}>
            {children}
        </div>
    )
}

// 리렌더링은 Scroll에서만 일어난다. 이 리렌더링은 props.children인 VerySlowComponent에는 영향을 주지 않는다. 
function Component() {
    return (
        <Scroll>
            <VerySlowComponent />
        </Scroll>
    )
}

Component as props

  • 위에와 아주 비슷한 패턴이다. 다만 children이 아닌 props일 뿐.
function Component() {
    const [state, setState] = useState()

    return (
        <div onScroll={(e) => setState(e)}>
            <VerySlowComponentOne />
            <Something />
            <VerySlowComponentTwo />
        </div>
    )
}
function Scroll({ left, right }) {
    const [state, setState] = useState()
    return (
        <div onScroll={(e) => setState(e)}>
            {left}
            <Something />
            {right}
        </div>
    )
}

// 리렌더링은 Scroll에서만 일어난다. 이 리렌더링은 props.left, props.right인 VerySlowComponent들에는 영향을 주지 않는다. 
function Component() {
    return (
        <Scroll 
            left={<VerySlowComponentOne />}
            right={<VerySlowComponentTwo />}
        />
    )
}
  • props가 리렌더링에 영향을 주는 예외 케이스는 메모이제이션된 컴포넌트일 경우다. 이 케이스는 다음 편에 마저 작성할 예정.

Ref

https://www.developerway.com/posts/react-re-renders-guide

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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 29 30
글 보관함