React 성능 최적화를 위한 useMemo, useCallback

Stems·2022년 12월 6일
0

React 최적화

목록 보기
1/1
post-thumbnail

React 성능 최적화

(정확한 설명은 아닐수도 있으니 주의하세요!)

1. useMemo

의존된(dependencies)의 값이 바뀌지 않았다면 기존에 렌더링시 연산된 값을 재사용 한다.
  const average = useMemo(() => {
    console.log("calculate average. It takes long time !!");
    return users.reduce((acc, cur) => {
      return acc + cur.score / users.length;
    }, 0);
  }, [users]);

여기서는 users가 debs다. users가 바뀌지 않으면 기존의 값을 재사용하여 성능을 최적화한다.

2. React.memo

먼저 최상위의 App.jsx가 있다.

function App() {
  const [value, setValue] = useState("");
  const [secondValue, setSecondValue] = useState("");

  // useCallback을 사용한 Input 컴포넌트 핸들러
  const handleChange = useCallback((e) => {
    setValue(e.target.value);
  }, []);

  const handleClick = useCallback(() => {
    console.log("click");
  }, []);
// --------------------------------------
  
  // useCallback을 사용하지 않은 SecondInput 컴포넌트 핸들러
  const secondChange = (e) => {
    setSecondValue(e.target.value);
  };

  const secondClick = () => {
    console.log("secondClick");
  };
  // ------------------------------------

  return (
    <div className="App">
      <TwoInput
        value={value}
        secondValue={secondValue}
        handleChange={handleChange}
        handleClick={handleClick}
        secondChange={secondChange}
        secondClick={secondClick}
      />
    </div>
  );
}

export default App;
const TwoInput = ({
  value,
  handleChange,
  handleClick,
  secondValue,
  secondChange,
  secondClick,
}) => {
  return (
    <div className="App">
      <Input
        value={value}
        handleChange={handleChange}
        handleClick={handleClick}
      />
      <SecondInput
        secondValue={secondValue}
        secondChange={secondChange}
        secondClick={secondClick}
      />
    </div>
  );
};

export default React.memo(TwoInput);
const Input = ({ value, handleChange, handleClick }) => {
  const text = value;
  console.log("input");
  return (
    <div
      style={{
        width: "500px",
        height: "300px",
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        border: "1px solid #eee",
      }}
    >
      <input
        type="text"
        style={{ width: "300px" }}
        value={text}
        onChange={handleChange}
      />
      <button onClick={handleClick} style={{ width: "30px", height: "20px" }} />
    </div>
  );
};

export default memo(Input); // <- props가 얕은비교를 통해 값이 바뀌지 않으면 리렌더하지 않는다.
const SecondInput = ({ secondValue, secondChange, secondClick }) => {
  console.log("SecondInput");
  return (
    <div
      style={{
        width: "500px",
        height: "300px",
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        border: "1px solid #eee",
      }}
    >
      <input
        type="text"
        style={{ width: "300px" }}
        value={secondValue}
        onChange={secondChange}
      />
      <button style={{ width: "20px", height: "20px" }} onClick={secondClick} />
    </div>
  );
};

export default SecondInput; // <- 항상 리렌더링

최적화가 된 컴포넌트는 Input, 안된 컴포넌트는 SecondInput 컴포넌트이다.
각각 컴포넌트에 input과 버튼에 value에 state, change 핸들러를 prop으로 넘겨주고 있다.

우리는 Input 컴포넌트에 있는 input에 입력을 할때 SecondInput에는 리렌더가 발생하지 않기를 원한다.
하지만 React dev tools로 랜더된 영역을 보면 SecondInput 컴포넌트도 리렌더가 되는것을 볼 수 있다.

그 이유는 부모 컴포넌트(TwoInput)의 State가 바뀌면서 부모 컴포넌트가 리렌더되기 때문이다.

React에서 렌더되는 3가지 조건
1. State 변경이 있을 때
2. 새로운 prop이 들어올 때
3. 부모 컴포넌트가 렌더링 될 때
prop 비교는 string, number의 얕은 비교를 한다. 
그러면 Object나 함수는 렌더될때마다 메모리 참조값이 바뀌는데 어떻게 메모이징할까?

함수에 useCallback으로 debs가 바뀔 때에만 재호출하여 새로 참조하게 한다.
재호출하지 않으면 메모리에 참조값이 기존과 같으므로 얕은비교를 통해도 같은값이다.

  const handleChange = useCallback((e) => {
    setValue(e.target.value);
  }, []);

그렇다면 useMemo와 useCallback을 사용한 Input 컴포넌트는 뭐가 다를까?
결과부터 보자

SecondInput 컴포넌트의 input에 입력을 했는데 Input 컴포넌트가 리렌더되지 않았다.
그 이유는 export default React.memo(Input)를 해줬기 때문이다.

memo는 컴포넌트에 들어오는 prop의 값이 변하지 않으면 리렌더를 하지 않는다.

SecondInput 컴포넌트는 prop이 바뀌었지만(secondValue) Input의 prop은 바뀌지 않았다.

마무리

자주 값이 바뀌지 않는경우라면 memo를 활용하자
하지만 이것 또한 비용이므로 아무 이유없이 남발하지 말자.

https://ui.toast.com/weekly-pick/ko_20190731
https://velog.io/@shin6403/React-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EB%8A%94-7%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95-Hooks-%EA%B8%B0%EC%A4%80

profile
- Steadily, Don't Stop

0개의 댓글