무언갈 만들지 않고, 이론만 정리하다보니 따분한 감이 없지 않다.

하지만 이런 과정도 있어야, 나중에 이해가 빠르다. 확실히! 🙆🏻‍♀️

이번 장은 훅스(hooks)에 대해 알아본다.

🍭 hooks

hooks는 함수형 컴포넌트에서도 클래스형 컴포넌트 기능을 사용할 수 있게 한다.

hooks를 통해 함수형 컴포넌트에서도 상탯값을 관리할 수 있다!

로직을 재사용하는 기존 방식의 한계

로직의 재사용은 주로 고차 컴포넌트와 렌더 속성값 패턴으로 이루어져 리액트 트리가 깊어진다.

따라서 성능에 영향을 주고, 개발 시 디버깅에 어려움을 준다.

클래스형 컴포넌트의 한계

서로 연관성 없는 여러 로직을 하나의 LifeCycle메소드로 작성할 때가 많다.

게다가 componentDidMount()를 등록하고, componentWillUnmount()를 깜빡할 때가 많다.

또한, 함수형 컴포넌트에 상탯값, LifeCycle을 주기 위해 클래스형으로 작성하는 것도 상당히 귀찮다.

hooks의 장점

물론, 위의 한계를 해결하는 것이 장점이다.

재사용 가능한 로직을 간편하게 만들어, 다른 개발자의 훅스도 쉽게 가져와 사용할 수도 있다.

또, 같은 로직을 한 곳으로 모을 수 있어 가독성이 좋고, 정적 타입으로 정의하기도 쉽다.

🍎 useState

state가 들어가는걸 보니 상탯값을 관리하나보다...하고 생각하면 된다!

간단한 예시로 시작한다...!

input창에 넣는 값이 그대로 <p>태그에 출력된다.

배열의 첫 번째는 상탯값, 두 번째는 이를 변경할 수 있는 함수를 넣는다.

비구조화 할당으로 각 원소에 이름을 준다.

키보드 입력시 마다 값이 업데이트 되므로 성능에 영향을 줄 수 있다.

이는 뒤에 정리할 useCallback으로 해결한다.

import React, { useState } from 'react';

function Profile() {
  const [name, setName] = useState('sohee');
  const [age, setAge] = useState(27); 
  return (
    <div>
      <p>{`My Name is ... ${name}`}</p>
      <p>{`My age is ... ${age} years old`}</p>
      <input type="text" value={name} onChange={e => setName(e.target.value)} />
      <input type="number" value={age} onChange={e => setAge(e.target.age)} />
    </div>
  )
}
export default Profile;

image.png

🍏 useEffect

useEffect는 함수형 컴포넌트에서 LifeCycle 메소드를 사용할 수 있게 한다.

간단한 예시로 먼저 사용법을 익힌다.

증가버튼을 클릭할 때마다 렌더링 되면서 useEffect 훅에 입력된 함수가 호출된다.

따라서 웹타이틀의 count가 1씩 증가한다.

import React, { useState, useEffect } from 'react';

function EffectComp() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `업데이트 횟수: ${count}`
  });
  return <button onClick={() => setCount(count + 1)}>증가</button>;
}

export default EffectComp;

image.png

🍐 API 호출

클래스의 LifeCycle메소드에서 사용하는 API호출을 훅스를 이용하여 함수형에서도 사용해보자!

useEffect의 훅에서 API통신을 하여, 받아온 데이터는 user상탯값에 저장한다.

useEffect훅에 입력된 함수는 렌더링할 때마다 호출되기 때문에 너무 자주 호출된다.

이를 위해 두 번째 매개변수에 배열을 입력하면, 배열 값이 변경될 때에만 호출된다.

import React, { useState, useEffect } from 'react';

function GetAPI({ userId }) {
  const [user, setUser] = useState(null);
  useEffect(() => {
    getUserApi(userId).then(data => setUser(data));
  },
  [userId]);

  return (
    <div>
      {!user && <p>사용자 정보 가져오는 중...</p>}
      {user && (
        <>
          <p>{`name is ${user.name}`}</p>
          <p>{`age is ${user.age}`}</p>
        </>
      )}
    </div>
  )
}

export default GetAPI;

🍊 이벤트 처리 함수 등록 및 해제

클래스형에서는 componentDidMount(), componentWillUnmount()로 이벤트를 등록, 해제시킨다.

훅스는 이러한 절차를 한곳에서 관리한다.

import React, { useState, useEffect } from 'react';

function WidthPrinter() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const onResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', onResize);
    return () => {
      window.removeEventListener('resize', onResize);
    };
  }, []);
  return <div>{`현재 화면의 넓이는 ${width} 입니다...!`}</div>
}

export default WidthPrinter;

창 크기가 변경될 때마다 onResize함수가 호출된다.

useEffect훅의 첫 번째에 등록된 함수가 또 다른 함수를 반환할 수 있다.

반환된 함수는 컴포넌트가 언마운트되거나 첫 번째 입력된 함수가 호출되기 직전에 호출된다.

즉, 첫 번째 입력된 함수가 반환한 함수는 비정상적인 종료가 아니라면 반드시 호출될 것이 보장된다.

두 번째 매개변수에 빈 배열을 넣으면 컴포넌트가 마운트 될 때만 첫 번째 매개변수로 입력된 함수가 호출되고, 언마운트 될 때만 반환된 함수가 호출된다.
image.png

🍋 두 기능 합치기

API 호출 기능과 이벤트 처리 함수를 등록 및 해제하는 기능을 합쳐서 만들 수 있다.

훅을 사용하면 다음 과 같이 로직별로 코드를 모을 수 있다!👍🏻

function Composition({ userId }) {
  const [user, setUser] = useState(null);
  useEffect(() => {
    getUserApi(userId).then(data => setUser(data));
  }, [userId]);
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const onResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);
  return // ...
}