[Learning React] 7장 훅스로 컴포넌트 개선하기

·2022년 10월 19일
0

Learning React

목록 보기
5/5

📌 useEffect

✏️ useEffect란?

렌더링은 앱이 처음 적재될 때 일어나고, props나 상태 값이 변경될 때 일어난다.
렌더링이 끝난 후 무언가를 하고 싶을 때가 있는데, 그럴 때 useEffect를 사용할 수 있다.
useEffect는 렌더러가 렌더링을 한 직후에 부수효과(side effect)로 useEffect 안에 있는 함수를 실행시킨다.
UI 렌더링 외에 컴포넌트가 수행해야 하는 일을 효과(effect)라고 부른다.

useEffect를 사용하면 렌더링이 끝나기를 기다렸다가 useEffect 안의 함수 값을 제공할 수 있다.
렌더링이 이뤄질 때마다 useEffect는 렌더링된 props, 상태, 참조 등의 최종 값에 접근할 수 있다.

✏️ 의존 관계 배열

렌더링이 이뤄질 때마다 모든 효과가 호출되는 것을 바라진 않을 것이다.
useEffect 훅을 구체적인 데이터 변경과 연동시키기 위해 의존 관계 배열을 사용한다.
의존 관계 배열은 효과가 호출되는 시점을 제어한다.

의존 관계 배열은 배열일 뿐이므로, 의존 관계 배열의 여러 값을 검사할 수 있다.

useEffect(() => {
  console.log("either val or phrase has changed");
}, [val, phrase]);

빈 의존 관계 배열은 초기 렌더링 직후 효과가 단 한 번만 호출되게 한다.
의존 관계가 없기 때문에 변경에 반응하지 않아 최초 렌더링시에만 호출되고 다시 호출되지 않는다.

useEffect(() => {
  console.log("only once after initial render");
}, []);

📝 문자열과 객체, 배열, 함수

의존 관계 배열에서는 문자열과 객체, 배열, 함수 등 모든 변화를 감시하게 할 수 있다.
그러나 문자열과 객체, 배열, 함수의 차이점을 주의해야 한다.

📋 문자열 비교

if ("gnar" === "gnar") {
  console.log("gnarly!!");
}

문자열은 다른 문자열과 동등한지 위 코드와 같이 비교할 수 있다.
위 코드에서는 당연히 두 문자가 동등하다!

📋 객체, 배열, 함수 등 비교

if ([1, 2, 3] !== [1, 2, 3]) {
  console.log("but they are the same");
}

자바스크립트에서 배열, 객체, 함수는 서로 같은 인스턴스일 때만 서로 같다.
따라서 위 코드에서는 길이나 원소가 모두 같지만 서로 다른 배열 인스턴스이므로 같지 않다.

useEffect 의존 관계 배열에 배열을 넣는 경우를 살펴보자!

const words = ["sick", "powder", "day"]

useEffect(() => {
  console.log("fresh render")
}, [words])

words라는 변수는 배열을 가리키기 때문에, useEffect가 렌더링이 이뤄질 때마다 매번 새로 생성된 배열 인스턴스를 바라보고 있어 "fresh render"가 매번 호출된다.

words를 함수 영역 밖에 정의하면 이 문제가 해결되지만, 이 해결법이 항상 가능하거나 항상 추천되진 않는다.
이러한 문제를 해결해주는 훅스가 useMemo이다!

📌 useMemo

useMemo는 메모화된 (memoized) 값을 계산하는 함수를 호출한다.
메모화된 함수는 함수 호출 결과를 저장하고 캐시한다. 그 후 함수에 같은 입력이 들어오면 캐시된 값을 반환한다.
리액트에서 useMemo를 사용하면 캐시된 값과 계산한 값을 비교해서 실제 값이 변경됐는지 검사해준다.
useMemo도 의존 관계 배열에 의존한다.

function WordCount({children: ""}) {
  const words = useMemo(() => {
    children.split(" ")
  },[children]);
  
  useEffect(() => {
    console.log("fresh render");
  },[words]);
  
  return (...);
 }

words 배열은 children property에 의존하므로 children이 바뀌면 words의 값도 재계산해야 한다.
useMemo는 컴포넌트가 최초 렌더링될 때와 children property가 바뀔 때 words를 다시 계산한다.

📌 useCallback

useCallback도 useMemo와 비슷한 기능을 하지만, 값 대신 함수를 메모화한다.
useCallback도 두 번째 인자로 의존 관계 배열을 받는다.

function WordCount({children: ""}) {
  const fn = useCallback(() => {
    console.log("hello");
    console.log("world");
  },[]);
  
  useEffect(() => {
    console.log("fresh render");
    fn();
  },[fn]);
  
  return (...);
 }

📌 useLayoutEffect

useLayoutEffect는 렌더링 사이클의 특정 순간에 호출된다.
useLayoutEffect는 렌더링 다음에 호출되지만, 브라우저가 변경 내역을 화면에 그리기 전에 호출된다.
창의 크기가 바뀐 경우 엘리먼트의 너비와 높이를 얻고 싶은 경우, 마우스 위치를 추적하고 싶은 경우 등에 useLayoutEffect를 사용할 수 있다.

  1. 렌더링
  2. useLayoutEffect 호출
  3. 브라우저 화면 그리기 (컴포넌트에 해당하는 엘리먼트가 DOM에 추가됨)
  4. useEffect 호출

📌 useReducer

✏️ Reducer란?

Reducer란 현재 상태를 받아서 새 상태를 반환하는 함수를 말한다.

checked => !checked

✏️ useState 대신 useReducer 사용하기

function checkbox() {
  const [checked, toggle] = useReducer(checked => !checked, false)
  
  return(
    <>
    <input type="checkbox" value={checked} onChange={toggle} />
    </>
  )
}

useReducer는 리듀서 함수와 초기 상태 false를 받는다.
onChange 함수를 리듀서가 반환하는 두 번째 값인 toggle로 설정하며, 이 함수는 리듀서 함수를 호출해준다.

📌 memo

memo는 순수 컴포넌트를 만들 때 쓰인다
(순수 컴포넌트 ? 같은 프로퍼티에 대해 항상 같은 출력으로 렌더링 되는 컴포넌트)

프로퍼티가 바뀌지 않았는데 순수 컴포넌트를 다시 렌더링하는 것은 효율적이지 않으므로 memo를 사용하여 프로퍼티가 변경될 때만 다시 렌더링 되는 컴포넌트를 만들 수 있다

const Cat = ({name}) => {
  console.log(`rendering ${name}`);
  return <p> {name} </p>
};

const PureCat = memo(Cat)

memo 함수에 전달되는 두 번째 인자에는 술어가 들어간다.
술어 함수가 false를 반환하면 Cat이 다시 렌더링되며, true를 반환하면 Cat이 다시 렌더링되지 않는다.

const PrueCat = memo(
  Cat, (prev, next) => prev.name === next.name
);
profile
개발을 개발새발 열심히➰🐶

0개의 댓글