이설아
Study

3.1 리액트의 모든 훅 파헤치기

useState

함수형 컴포넌트 내부에서 상태를 정의하고, 해당 상태를 관리할 수 있게 해주는 훅

import { useState } from 'react’;
 
const [state, setState] = useState(initialState);
  • 아무런 값을 넘겨주지 않으면 초기값은 undefined
  • 반환값은 배열
  • 배열의 첫 번째 원소로 state 값 자체를 사용
  • 두번째 원소인 setState 함수를 사용해 해당 state의 값 변경 가능
  • 클로저를 사용하여 setState가 호출된 이후에도 지역변수인 state 참조 가능

게으른 초기화(lazy initialization)

useState에 변수 대신 함수를 넘기는 것

// 일반적인 useState사용
// 바로 값을 집어넣음
const [count, setCount] = useState(
  Number.parselnt(window.localStorage.getltem(cacheKey))
);
 
// 게으른 초기화
// 위 코드와의 차이접은 함수를 실행해 값을 반환
const [count, setCount] = useState(() =>
  Number.parselnt(window.localStorage.getltem(cacheKey))
);
  • useState의 초깃값이 복잡하거나 무거운 연산을 포함하고 있을 때 사용 -공식문서
  • 해당 함수는 오로지 state가 처음 만들어질 때만 사용

useEffect

애플리케이션 내 컴포넌트의 여러 값들을 활용하여 동기적으로 부수 효과를 만드는 메커니즘

function Component() {
  // ...
  useEffect(() => {
    // do something
  }, [props, state]);
  // ...
}
  • 첫 번째 인수: 실행할 부수효과가 포함된 함수

  • 두 번째 인수: 의존성 배열

  • 의존성 배열이 변경될 때 마다 콜백 실행

    ⇒ state와 props의 변화 속에서 일어나는 렌더링 과정에서 실행되는 부수효과

사용 시 주의할 점

  1. eslint-disable-line react-hooks/exhaustive-deps 주석은 최대한 자제하라
  2. useEffect의 첫 번째 인수에 함수명을 부여하라
  3. 거대한 useEffect를 만들지 마라
  4. 불필요한 외부 함수를 만들지 마라

클린업 함수의 목적

클린 업: 어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하는 과정

이벤트를 등록하고 지울 때 사용

  • 새로운 값을 기반으로 렌더링 뒤에 실행되지만 변경된 값을 읽는 것이 아닌 함수가 정의됐을 당시에 선언된 값을 보고 실행

useMemo

비용이 큰 연산에 대한 결과를 저장 후 저장된 값을 반환

import { useMemo } from ’react’;
 
const memoizedValue = useMemo(
	() => expensiveComputation(a, b), [a, b]);
  • 컴포넌트의 성능을 최적화 하는데 사용
  • 렌더링 시 의존성 배열의 값이 변경되지 않음 → 함수 재실행 x, 기억해둔 해당 값 반환
  • 렌더링 시 의존성 배열의 값이 변경됨 → 첫번째 인수의 함수 실행, 해당 값 반환, 해당 값 저장

UseCallback

특정 함수를 새로 만들지 않고 다시 재사용

const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);
  • 함수의 재생성을 막아 불필요한 리소스 또는 리렌더링을 방지하고 싶을 때 사용

useRef

저장공간 또는 DOM에 접근하기 위한 훅

  • setState와의 차이점
    1. useRef는 반환값인 객체 내부에 있는 current로 값에 접근 또는 변경 가능
    2. useRef는 그 값이 변하더라도 렌더링 발생 X
function RefComponent() {
  const count = useRef(0);
 
  function handleClick() {
    count.current += 1;
  }
 
  // 버튼을 아무리 눌러도 변경된 count 값이 렌더링되지 않는다
  return <button onClick={handleClick}>{count.current}</button>;
}
  • 컴포넌트가 렌더링될 때만 생성되며, 컴포넌트 인스턴스가 여러 개라도 각각 별개의 값을 바라봄
  • useRef의 최초 기본값은 useRef()로 넘겨받은 인수

useContext

contxt와 해당 context를 함수형 컴포넌트에서 사용할 수 있게 하는 훅

  • 다수의 Provider와 useContext 사용 시, 별도 함수로 감싸서 사용하는 것이 좋음

useContext 사용 시 주의 점

  • useContext를 함수형 컴포넌트 내부에서 사용할 때는 컴포넌트 재활용이 어려워진다는 것을 염두해두기

    ⇒ useContext가 있는 컴포넌트는 Provider와의 의존성을 갖게 되는 셈

useReducer

useState를 대체할 수 있는 함수로 복잡한 상태관리가 필요한 경우 사용

import React, { useReducer } from 'react';
 
const [state, dispatch] = useReducer(reducer, initialState, init);
  • state 값을 변경하는 시나리오를 제한적으로 두고 이에 대한 변경을 빠르게 확인할 수 있게 하는 것

용어 정리

  • state
    • 컴포넌트에서 사용할 상태
  • dispatch 함수
    • 첫번째 인자인 reducer 함수를 실행
    • 컴포넌트 내에서 state의 업데이트를 일으키키 위해 사용하는 함수
  • reducer 함수
    • 컴포넌트 외부에서 state를 업데이트 하는 함수
    • 현재 state, action 객체를 인자로 받아, 기존의 state를 대체하여 새로운 state를 반환하는 함수
  • initialState
    • 초기 state
  • init
    • 초기 함수(초기 state를 조금 지연해서 생성하기 위해 사용)

useState와 useReduce의 차이점

  • 세부 동작과 쓰임에만 차이가 있음

    ⇒ 클로저를 활용하여 state를 관리하는 것은 같음

useImperativeHandle

부모에게서 넘겨받은 ref를 원하는 대로 수정할 수 있는 훅

useLayoutEffect

useEffect와 동일하지만, 렌더링 후 layout과 paint 전에 동기적으로 실행

실행 순서

  1. 리액트가 DOM을 업데이트
  2. useLayoutEffect 실행
  3. 브라우저에 변경사항을 반영
  4. useEffect 실행

Alt text

  • useEffect: mount 직후(painting과 비동기로 실행)

  • useLayoutEffect: redering 직후 mount(painting) 전에 실행

    ⇒ DOM은 계산 되었지만 화면에 반영되기 전에 하고 싶은 작업이 있을 때 사용

useDebugValue

디버깅 하고 싶은 정보를 해당 훅에 사용하여 확인

  • 사용자 정의 훅 내부의 내용에 대한 정보를 남길 수 있음

  • 두 번째 인수로 포매팅 함수 전달 → 해당 값이 변경되었을 때만 포매팅 값 노출

    ⇒ 첫번째 인수의 값이 같으면 포매팅 함수 호출 X

  • 오직 다른 훅 내부에서만 실행할 수 있음

훅의 규칙

  1. 최상위에서만 흑을 호출해야한다.

    반복문이나 조건문, 중칩된 함수 내에서 훅을 실행할 수 없다.

    이 규칙을 따라야만 컴포넌트가 렌더링될 때마다 항상 동일한 순서로 혹이 호출되는 것을 보장할 수 있다.

  2. 훅을 호출할 수 있는 것은 리액트 함수형 킴포넌트 혹은 사용자 정의 훅의 두 가지 경우뿐이다.

    일반 자바스크립트 할수에 서는 혹을 사용할수 없다.

3.2 사용자 정의 훅과 고차 컴포넌트 중 무엇을 써야 할까?

사용자 정의 훅

서로 다른 컴포넌트 내부에서 같은 로직을 공유하고자 할 때 사용하는 것

  • 리액트 훅의 규칙을 따르고 react-hooks/rules-of-hooks의 도움을 받기 위해서는 use로 시작하는 함수를 만들어야 함

고차 컴포넌트(HOC)

컴포넌트 자체의 로직을 재사용하기 위한 방법

  • 고차함수의 일종으로 리액트가 아니더라도 JS 환경에서 사용 가능

  • 컴포넌트의 결과물에 영향을 미칠 수 있는 다른 공통된 작업을 처리 가능

  • with로 시작하는 이름을 사용해야 함

    ⇒ 강제성이 아닌 관습

  • 부수효과를 최소화 하기

    • HOC는 반드시 컴포넌트를 인수로 받는데, 컴포넌트의 props를 임의로 수정, 추가, 삭제하는 일은 없어야 함
  • 반복적으로 컴포넌트를 감쌀 경우 복잡성이 커져, 최소한으로 사용하기

사용자 정의 훅 vs. 고차 컴포넌트

사용자 정의 훅

  • 리액트에서 제공하는 훅으로만 공통 로직을 격리할 수 있을 때

  • 컴포넌트 내부에 미치는 영향 최소화

    → 훅을 원하는 방향으로만 사용할 수 있음

고차 컴포넌트

  • 렌더링의 결과물에도 영향을 미치는 공통 로직일 때
  • 고차 컴포넌트가 많아질수록 복잡성이 기하급수적으로 증가하므로 신중하게 사용