React 재 렌더링 정보

18656 단어 React

전치



React를 배우기 시작했을 때 이해가 얕은 memo화에 대해 정리해 보았습니다.

재렌더링이란?



변경 사항을 감지하고 구성 요소를 다시 처리하는 것입니다.
화면을 다시 로드한 것도 아니지만, 처리에 의해 화면이 변경되는 것은, 컴퍼넌트가 재렌더되고 있기 때문입니다.



재 렌더링이 발생하는 조건


  • state가 갱신되었을 때
  • Props가 변경되었을 때
  • 다시 렌더링 된 구성 요소 아래의 모든 하위 구성 요소

  • 3은 보통 이미지가 다루기 어렵지만, 아래의 예와 같이 루트 컴포넌트인 App.jsx가 렌더링되면 그 아래의 컴포넌트가 모두 렌더링되어 버립니다.
    변경할 필요가 없는 구성 요소까지 렌더링되면 앱 자체의 성능이 저하됩니다.


    React.memo, useCallback을 사용하여 불필요한 렌더링 방지



    여기에서 실제로 코드를 사용하여 설명합니다. 이 예제에서는 App.js를 부모로, 하위 구성 요소를 Child.jsx로 가정합니다.
    // App.js
    import { useState } from "react";
    import Child from "./Child";
    import "./styles.css";
    
    export default function App() {
      const [number, setNumber] = useState(0);
      const onClickNumber = () => setNumber(number + 1);
      console.log("レンダリングしてます");
      return (
        <div className="App">
          <button onClick={onClickNumber}>ボタン</button>
          <h1>{number}</h1>
          <Child />
        </div>
      );
    }
    
    
    // Child.jsx
    const Child = () => {
      return (
        <>
          {console.log("child")}
          <div>childです</div>
        </>
      );
    };
    export default Child;
    
    

    부모 컴포넌트에 있는 버튼을 클릭하면, 아이인 Child.jsx에 넣은 console.log도 클릭할 때마다 발화해 버립니다.
    불필요한 렌더링이 일어나고 있으므로, 아이 컴퍼넌트는 변경이 있었을 경우만 렌더링 시키고 싶습니다.



    React.memo



    컴퍼넌트 자체를 memo로 둘러싸는 것만으로 props에 변경이 없는 한 그 컴퍼넌트로 재렌더링이 일어나지 않게 됩니다.
    // child.jsx
    
    // 追加
    import { memo } from "react";
    
    // memoで囲うだけ!
    const Child = memo(() => {
      return (
        <>
          {console.log("child")}
          <div>childです</div>
        </>
      );
    });
    export default Child;
    
    

    버튼을 클릭해도 하위 구성 요소의 console.log가 더 이상 발화되지 않습니다.



    useCallback



    아이 컴퍼넌트에 화살 함수를 건네주면, memo화해도 렌더링 되게 됩니다.
    이를 useCallback을 활용하여 한 번 정의한 화살 함수가 업데이트되지 않는 한 동일한 것을 사용하는 처리를 실현할 수 있습니다.
    // 書き方
    useCallback(アロー関数, [依存配列]);
    

    시험에 버튼을 클릭하면, 아이 컴퍼넌트를 표시 비표시하는 구현으로 변경합니다.



    그 때, 부모 컴퍼넌트에 있는 애로우 함수를 props로 건네줍니다.
    // App.js
    import { useState } from "react";
    import Child from "./Child";
    import "./styles.css";
    
    export default function App() {
      const [number, setNumber] = useState(0);
      const [show, setShow] = useState(false);
      const onClickNumber = () => setNumber(number + 1);
      // 追加
      const onClickShow = () => setShow(!show);
      const onClickClose = () => setShow(false);
    
      return (
        <div className="App">
          <button onClick={onClickNumber}>ボタン</button>
          <h1>{number}</h1>
          <button onClick={onClickShow}>show/hide</button>
          {/* アロー関数をpropsで渡す */} 
          <Child show={show} onClickClose={onClickClose} />
        </div>
      );
    }
    
    
    // Child.jsx
    import { memo } from "react";
    
    const Child = memo((props) => {
      const { show, onClickClose } = props;
      return (
        <>
          {console.log("child")}
          {show ? (
            <>
              <div>childです</div>
              <button onClick={onClickClose}>閉じる</button>
            </>
          ) : null}
        </>
      );
    });
    export default Child;
    
    

    콘솔을 보면 자식 구성 요소에 있는 console.log가 다시 불이 났음을 알 수 있습니다.
    memo화했음에도 불구하고, 부모 컴퍼넌트에 있는 버튼을 클릭하면, 관계가 없는 아이 컴퍼넌트도 다시 렌더링 되게 되어 버렸습니다.


    이것은 화살 함수가 렌더링 될 때마다 재생성되므로 매번 다른 함수로 인식됩니다.
    그 때문에, props가 바뀌고 있다고 판단되므로 아이 컴퍼넌트도 재렌더링이 일어나게 됩니다.

    처리의 흐름으로서는 다음과 같이 됩니다.
    1. 부모의 버튼을 클릭합니다.
    2. state가 변경되기 때문에, 부모 컴퍼넌트가 렌더링 된다
    3. 렌더링하면 부모 컴포넌트의 애로우 함수가 재생성된다
    4. 재생성 된 화살표 함수를 자식 구성 요소로 전달
    5. props가 변경되었다고 판단되기 때문에, 아이 컴퍼넌트도 렌더링 된다

    useCallback을 사용하면 불필요한 렌더링을 방지할 수 있습니다.
    // App.js
    // 追加
    import { useState, useCallback } from "react";
    import Child from "./Child";
    import "./styles.css";
    
    export default function App() {
      const [number, setNumber] = useState(0);
      const [show, setShow] = useState(false);
      const onClickNumber = () => setNumber(number + 1);
      const onClickShow = () => setShow(!show);
    // 追加
      const onClickClose = useCallback(() => setShow(false), [setShow]);
    
      return (
        <div className="App">
          <button onClick={onClickNumber}>ボタン</button>
          <h1>{number}</h1>
          <button onClick={onClickShow}>show/hide</button>
          <Child show={show} onClickClose={onClickClose} />
        </div>
      );
    }
    
    

    이제 불필요한 렌더링을 방지 할 수있었습니다!

    좋은 웹페이지 즐겨찾기