Part9_Hooks
이효석
책 읽기

9장. 훅

9.1 리액트 훅

  • 리액트 훅 추가 이전에는 프로젝트 규모가 커질 수록 생명 주기 함수에서만 로직을 구현해야 했기 때문에, 비슷한 로직을 재사용 할 수 없는 문제 그리고 사이드 이펙트가 발생
  • 리액트 훅의 도입으로 비지니스 로직을 재사용하거나 테스트 하는 것이 용이해짐

9.1.1 useState

  • useState 와 타입 스크립트를 사용하면 잘못된 속성의 추가로 인하여 예기치 못한 사이드 이펙트가 생기는 것을 방지할 수 있다
import { useState } from "react";
 
interface Member {
  name: string;
  age: number;
}
 
const MemberList = () => {
  const [memberList, setMemberList] = useState<Member[]>([]);
 
  // member의 타입이 Member 타입으로 보장된다
  const sumAge = memberList.reduce((sum, member) => sum + member.age, 0);
 
  const addMember = () => {
  // 🚨 Error: Type ‘Member | { name: string; agee: number; }’
  // is not assignable to type ‘Member’
    setMemberList([
      ...memberList,
      {
        name: "DokgoBaedal",
        agee: 11,
      },
    ]);
  };
 
  return (
    // ...
  );
};

9.1.2 의존성 배열을 사용하는 훅

useEffect 와 useLayoutEffect

function useEffect(effect: EffectCallback, deps?: DependencyList): void;
type DependencyList = ReadonlyArray<any>;
// Destructor 는 컴포넌트 마운트가 해제 될 때 실행되는 함수, deps 배열의 변경에 따라 실행이 반복
type EffectCallback = () => void | Destructor;

useMemo 와 useCallback

type DependencyList = ReadonlyArray<any>;
 
function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
function useCallback<T extends (...args: any[]) => any>(
  callback: T,
  deps: DependencyList
): T;

9.1.3 useRef

자식 컴포넌트에 ref 전달하기

  • forwardRef 를 사용
  • ForwardedRef 에는 MutableRefObject 만 올 수 있으므로 RefObject 보다 넓은 타입이기 때문에 부모 컴포넌트에서의 ref 선언과 상관 없이 자식 컴포넌트가 ref 를 사용 가능
type ForwardedRef<T> =
  | ((instance: T | null) => void)
  | MutableRefObject<T | null>
  | null;

useImperativeHandle

  • 해당 훅을 활용하면 부모 컴포넌트에서 ref 를 통해 자식 컴포넌트에서 정의한 커스터마이징 메서드를 호출 가능
// useImperativeHandle 를 사용한 메서드 커스터마이징
type CreateFormHandle = Pick<HTMLFormElement, 'submit'>;
 
type CreateFormProps = {
  defaultValues?: CreateFormValue;
};
 
const JobCreateForm: React.ForwardRefRenderFunction<
  CreateFormHandle,
  CreateFormProps
> = (props, ref) => {
  // useImperativeHandle Hook을 사용해서 submit 함수를 커스터마이징한다
  useImperativeHandle(ref, () => ({
    submit: () => {
      /* submit 작업을 진행 */
    },
  }));
 
  // ...
};
 
// 자식 ref 를 불러와서 커스터마이징 된 메서드를 사용
const CreatePage: React.FC = () => {
  // `CreateFormHandle` 형태를 가진 자식의 ref를 불러온다
  const refForm = useRef<CreateFormHandle>(null);
 
  const handleSubmitButtonClick = () => {
    // 불러온 ref의 타입에 따라 자식 컴포넌트에서 정의한 함수에 접근할 수 있다
    refForm.current?.submit();
  };
 
  // ...
};

useRef 의 여러 가지 특성

9.2 커스텀 훅

9.2.1 나만의 훅 만들기

import { useState } from 'react';
 
const useInput = (initialValue) => {
  const [value, setValue] = useState(initialValue);
 
  const onChange = (e) => {
    setValue(e.target.value);
  };
 
  return { value, onChange };
};

9.2.2 타입스크립트로 커스텀 훅 강화하기

import { useState, useCallback, ChangeEvent } from 'react';
 
// ✅ initialValue에 string 타입을 정의
const useInput = (initialValue: string) => {
  const [value, setValue] = useState(initialValue);
 
  // ✅ 이벤트 객체인 e에 ChangeEvent<HTMLInputElement> 타입을 정의
  const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value);
  }, []);
 
  return { value, onChange };
};
 
export default useInput;