IT/React

useEffect, useMemo, useCallback 완벽히 이해하기

라임웨일 2022. 5. 31. 14:51
반응형

React Hook 주의점

  • 최상위 바깥에 선언해서 실행 순서가 일정하게 하는 것이 중요합니다.
  • 조건문 안에서 Hook를 실행시키지 않습니다.
  • hook안에 hook을 사용하지 않습니다.
  • Hook을 일반적인 JavaScript 함수에서 호출하지 않습니다.
    •  React 함수 컴포넌트에서 Hook을 호출하세요.
    •  Custom Hook에서 Hook을 호출하세요.

 

리액트에서 Hook을 반복문, 조건문 혹은 중첩된 함수 내에서 호출하지 않습니다.

Hook은 React 함수의 최상위(at the top level)에서 Hook을 호출해야 합니다. 이 규칙을 따르면 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장됩니다. 이러한 점은 React가 useState와 useEffect 가 여러 번 호출되는 중에도 Hook의 상태를 올바르게 유지할 수 있도록 해줍니다. 

 

😊 한 컴포넌트에서 State나 useEffect의 Hook을 여러 개 사용한 예

function Form() {
  // 1. name이라는 state 변수를 사용하세요.
  const [name, setName] = useState('Mary');

  // 2. Effect를 사용해 폼 데이터를 저장하세요.
  useEffect(function persistForm() {
    localStorage.setItem('formData', name);
  });

  // 3. surname이라는 state 변수를 사용하세요.
  const [surname, setSurname] = useState('Poppins');

  // 4. Effect를 사용해서 제목을 업데이트합니다.
  useEffect(function updateTitle() {
    document.title = name + ' ' + surname;
  });

  // ...
}

 

😢 조건문에 Hook을 사용함으로써 규칙을 위반한 예

if (name !== '') {
  useEffect(function persistForm() {
    localStorage.setItem('formData', name);
  });
}

조건문안에서 Hook를 사용하지 않아야 하는 이유는 React가 Hook이 호출되는 순서에 의존한다는 것입니다. 

 

name !== '' 조건은 첫 번째 렌더링에서 'true' 기 때문에 Hook은 동작합니다. 하지만 사용자가 그다음 렌더링에서 폼을 초기화하면서 조건을 'false'로 만들 겁니다. 이렇게 되면 리액트의 렌더링 간에 Hook을 건너뛰기 때문에 Hook 호출 순서는 달라지게 되고 처음 예상했던 결과와 다른 결과를 얻게 될 수 있습니다.

 

😢 랜더링 순서가 바뀌는 예

useState('Mary')           // 1. name state 변수를 읽습니다. (인자는 무시됩니다)
// useEffect(persistForm)  // 🔴 Hook을 건너뛰었습니다!
useState('Poppins')        // 🔴 2 (3이었던). surname state 변수를 읽는 데 실패했습니다.
useEffect(updateTitle)     // 🔴 3 (4였던). 제목을 업데이트하기 위한 effect가 대체되는 데 실패했습니다.

 

이것이 컴포넌트 최상위(the top of level)에서 Hook이 호출되어야만 하는 이유입니다. 조건부로 effect를 실행하기를 원한다면, 조건문을 Hook 내부에 넣을 수 있습니다.

 

😊 Hook 내부에 조건문을 사용

useEffect(function persistForm() {
  // 👍 더 이상 첫 번째 규칙을 어기지 않습니다
  if (name !== '') {
    localStorage.setItem('formData', name);
  }
});

 

useEffect

useEffect은 리액트 컴포넌트가 랜더링 될 때마다 특정 작업을 수행하도록 설정할 수 있는 hook입니다. 클래스형  컴포넌트

componentDidMount와 componentDidUpdate를 합친 형태로 보아도 무방합니다.

componentDidMount

useEffect에서 설정한 함수를 컴포넌트가 화면에 처음 랜더링 될 때만 실행하고, 업데이트될 때는 실행하지 않으려면 함수의 두 번째 파라미터로 빈 배열을 넣어주면 됩니다.

useEffect(() => {
  // 실행 할 로직
  console.log('마운트될 때만 실행됩니다.')
}, []);

componentDidUpdate

특정값이 변경될 때 호출하고 싶으면 두 번째 파라미터의 배열 안에 검사하고 싶은 값을 전달합니다.

useEffect(() => {  
  // 실행 할 로직
  console.log(name)
}, [name]);

cleanUp

useEffect은 리액트가 랜더링 되고 난 직후 최초 한 번은 실행됩니다. 이후 두 번째 파라미터 배열에 무엇을 전달하냐에 따라 조건이 달라집니다. 컴포넌트가 언마운트 되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶으면 useEffect에서 뒷정리(cleanup) 함수를 반환해 주어야 합니다.

useEffect(() => {  
  console.log('effect');
  console.log(name)
  return ()=>{
    console.log('cleanup');
    console.log(name)
  }
}, [name]);

위와 같이 코드를 작성하면 랜더링 될 때마다 뒷정리 함수가 실행되는 것을 확인할 수 있습니다. 그리고 뒷정리 함수가 실행될 때는 업데이트되기 직전의 값을 보여 줍니다.

 

오직 언마운트될 때만 뒷정리 함수를 호출하고 싶으면 두 번째 파라미터에 빈 배열을 전달하면 됩니다.

useEffect(() => {  
  console.log('effect');
  console.log(name)
  return ()=>{
    console.log('cleanup');
    console.log(name)
  }
}, []);

useMemo(callback, [변경되는 값])

useMemo를 사용하면 함수 컴포넌트에서 연산을 최적화 할 수 있습니다. 함수 컴포넌트는 화면의 값이 바뀌게 되면 매번 함수가 새로 그려지며 실행되기 때문에 변경되지 않은 화면도 다시 실행되면서 함수가 계속 호출되는 문제 발생합니다.

이러한 문제점을 방지하기 위해 useMemo를 사용합니다.  useMemo는 의존성이 변경되었을 때에만 메모이제이션된 값만 다시 계산하고 최적화는 모든 렌더링 시의 고비용 계산을 방지하게 해 줍니다.

  • 두 번째 배열이 바뀌기 전까지 값을 기억
  • 변경되는 값이 없다면 한 번만 실행 후 값을 보관하는 역할로 사용 가능
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useCallback(callback, [변경되는 값])

useCallback 은 우리가 지난 시간에 배웠던 useMemo와 비슷한 Hook입니다.

useMemo는 특정 결괏값을 재사용할 때 사용하는 반면, useCallback 은 특정 함수를 새로 만들지 않고 재사용하고 싶을 때 사용합니다.

  • 두 번째 배열이 바뀌기 전까지 함수 자체를 기억
  • 함수 생성 자체가 오래 걸리는 경우(=함수 내의 연산이 복잡한 경우) 쓰면 최적화에 도움됨
  • 변경되는 값이 없다면 state 값을 맨 처음 값만 기억(console로 확인)
  • 변경되는 값이 있을 때 새로운 값을 기억할 수 있음.
  • 자식 컴포넌트에 함수를 props로 내릴 때는 useCallback을 반드시 사용(자식 리렌더링 방지)
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

useMemo()와 useCallback() 차이점

  • useMemo : '함수 return 값'을 기억
  • useCallback : '함수 reference'를 기억
반응형