프론트엔드/실전 리액트 프로그래밍

[스터디 with 실전 리액트 프로그래밍] 14편 - 여러가지 훅(Hook)

Junheehee 2022. 8. 16. 18:54

실전 리액트 프로그래밍

 

이번엔 useState, useEffect 외의 다양한 훅을 알아보자.

 

 

 

 

useContext

 

useContext 훅은 Consumer 컴포넌트를 사용하지 않고도 Provider 컴포넌트의 context 데이터를 사용할 수 있게 해준다.

사용한 것과 사용하지 않은 것을 비교해보자.

 

// Provider 컴포넌트
import React, { createContext, useContext } from "react";

const UserContext = createContext();

export default function NoUseContext() {
  const user = { name: "Jane", age: "25" };
  return (
    <UserContext.Provider value={user}>
      <CunsumerChild />
      <UseContextChild />
    </UserContext.Provider>
  );
}


// Consumer 사용
function CunsumerChild() {
  return (
    <UserContext.Consumer>
      {(user) => (
        <>
          <h2>{`name is ${user.name}`}</h2>
          <h2>{`age is ${user.age}`}</h2>
        </>
      )}
    </UserContext.Consumer>
  );
}


// useContext 사용
function UseContextChild() {
  const user = useContext(UserContext);
  return (
    <>
      <h2>{`name is ${user.name}`}</h2>
      <h2>{`age is ${user.age}`}</h2>
    </>
  );
}

Consumer를 사용하면, Consumer 컴포넌트안에서만 context 데이터를 접근할 수 있어 불편하고 로직도 비교적 복잡하다.

반면, useContext는 context 데이터를 반환해 컴포넌트 안에서 편하게 사용할 수 있다.

 

 

 

 

 

useMemo

 

useMemo는 계산량이 많은 함수의 반환값을 재활용하는 용도로 사용된다.

 

import React, { useMemo, useState } from "react";

export default function Parent() {
  const [color, setColor] = useState("black");
  return (
    <>
      <button onClick={() => setColor("black")}>black</button>
      <button onClick={() => setColor("white")}>white</button>
      <TestUseMemo color={color} />
    </>
  );
}

function TestUseMemo({ color }) {
  const veryComplexFunc = (para) => {
    console.log(para);
    return para;
  };
  const value = useMemo(() => veryComplexFunc(color), [color]);
  return <h1>{value}</h1>;
}

useMemo의 첫번째 인수로는 함수를 입력한다.

useMemo는 이 함수의 반환값을 기억한다.

두번째 인수는 의존성 배열이다.

이 의존성 배열이 변경되지 않으면, 함수를 실행하지 않고 useMemo는 기억했던 반환값을 재사용한다.

 

여기서 주의할점은 함수의 인수가 변경될 때가 아닌, 의존성 배열이 변경될 때 함수를 실행한다는 점이다.

 

useMemo 사용

똑같은 버튼을 여러번 눌러도 의존성 배열인 color가 변경되지 않으니, 함수는 처음 한번만 실행된다.

 

 

useMemo가 반환값을 기억하는 것 자체도 메모리를 차지하기 때문에, 오래 걸리지 않는 함수나 값이 자주 바뀌어 기억할 필요가 없는 함수는 사용하지 않는 것이 좋다.

 

 

 

 

useCallback

 

useCallback도 useMemo와 같이 메모제이션과 관련된 훅이다.

useMemo가 반환값을 저장한다면. useCallback은 함수 자체를 저장한다.

 

 

function Parent() {
  const [variable, setVariable] = useState();
  const sampleFunc = (param) => {
    // ...
  };
  return (
    <Child sampleFunc={() => sampleFunc(variable)} />
  )
}

 

자식 컴포넌트에 함수를 전달하는 컴포넌트가 있다고 해보자.

이 컴포넌트는 렌더링될 때마다 매번 새로운 함수를 생성해서 넘겨준다.

(variable이 변하지 않아도 arrow function 자체가 새로운 함수를 생성한다.)

더군다나, 자식 컴포넌트에서 React.memo를 사용해 리렌더링을 막으려해도,
props가 매번 변경되니 sampleFunc이 똑같은 기능을 해도 불필요한 리렌더링이 일어난다.

 

이를 useCallback으로 해결해보자.

 

function Parent2() {
  const [variable, setVariable] = useState();
  const sampleFunc = (param) => {
    // ...
  };
  const func = useCallback(() => sampleFunc(variable), [variable]);
  return <Child sampleFunc={func} />;
}

useMemo와 마찬가지로 첫번쨰 인수는 함수, 두번째 인수는 의존성 배열이다.

이제 의존성 배열이 변경되지 않는다면 useCallback으로 기억한 이전 함수가 재사용된다.

 

 

 

 

useImperativeHandle

 

useImperativeHandle 훅은 자식 컴포넌트의 함수를 마치 메서드처럼 사용할 수 있게 해준다.

 

 

import React, { useImperativeHandle, useRef, useState } from "react";

export default function TestUseImperativeHandle() {
  const profileRef = useRef();
  const onClick = () => {
    if (profileRef.current) {
      profileRef.current.printProfile();
      profileRef.current.addAge(5);
    }
  };

  return (
    <>
      <Child ref={profileRef} />
      <button onClick={onClick}>나이 5 증가</button>
    </>
  );
}

const Child = React.forwardRef((props, ref) => {
  const [profile, setProfile] = useState({ name: "Mike", age: 30 });
  useImperativeHandle(ref, () => ({
    printProfile: () =>
      console.log(`name: ${profile.name}, age: ${profile.age}`),
    addAge: (age) => setProfile({ ...profile, age: profile.age + age }),
  }));
  return (
    <>
      <h2>name is {profile.name}</h2>
      <h2>age is {profile.age}</h2>
    </>
  );
});

useImperativeHandle 사용

ref 객체를 처리하기 위해 자식 컴포넌트를 forwarRef 함수로 감쌌다.

useImperativeHandle로 부모 컴포넌트에서 사용할 함수를 ref에 넣었다.

버튼을 클릭하니, 자식 컴포넌트의 함수가 잘 호출되었다.

 

 

 

 

그 외에 useEffect와 비슷하지만 함수를 동기로 호출하는 useLayoutEffect나,

디버깅에 도움을 줘 개발을 편하게 해주는 useDebugValue등이 있다.

 

 

 

 

코드

 

 

GitHub - junhee-won/react-study: with 실전 리액트 프로그래밍

with 실전 리액트 프로그래밍. Contribute to junhee-won/react-study development by creating an account on GitHub.

github.com