surf react : hooks (useState, useEffect) - 2

🔔 Goal

  • react hooks의 useState를 이해한다.
  • react hooks의 useEffect를 이해한다.

🌳 useState

useState는 함수형 컴포넌트에 state를 제공한다.

initialState를 파라미터로 받고, state와 state를 변경할 setState 함수를 반환한다.

import { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>{`count: ${count}`}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(count - 1)}>-</button>
    </div>
  )
};

export default Counter;

useState가 반환하는 첫 번째 인자인 state와 두 번째 인자인 setState를 비구조화 할당 문법을 통해 count, setCount로 받아서 사용할 수 있게 된다.

setCount로 count state를 변경하면 리렌더링된다.

Counter는 함수이기 때문에, 렌더링 할 컴포넌트 대신에 값을 반환할 수도 있다.

import { useState } from 'react';

const useCount = (gap) => {
  const [count, setCount] = useState(0);
  
  const increaseCount = () => {
    setCount(count + gap);
  }
  
  const decreaseCount = () => {
    setCount(count - gap);
  }
  
  return [
    count,
    increaseCount,
    decreaseCount,
  ]
}

export default useCount;

위 예제가 바로 Custom Hook을 만든 것이다. 이 useCount를 원하는 컴포넌트에서 호출하면 state인 count 값을 이용할 수 있고, setCount를 실행하도록 만들어진 increaseCount나 decreaseCount로 state를 변경할 수 있게 된다.

use~는 Custom Hook의 naming rule이다. 이 rule을 지키면 lint에서 hooks와 관련된 규칙들을 점검해줄 수 있기 때문에 따르는 것을 권장한다.

🌳 useEffect

클래스 컴포넌트에 제공되었던 Life Cycle API는 useEffect로 사용할 수 있다.

Life Cycle API에서 우리가 수행했던 API 요청, DOM 조작 등이 side effect이기 때문에, useEffect라는 이름의 API가 되었다고 한다.

클래스 컴포넌트에서의 componentDidMount, componentDidUpdate, componentWillUnmount가 useEffect로 실행된다.

function useEffect(effect: EffectCallback, inputs?: InputIdentityList)

render가 발생할 때 마다(componentDidMount: 초기, componentDUpdate: 매번) effect가 실행된다.

두 번째 파라미터인 inputs를 통해 특정한 상태가 update 되었을 때만 effect가 실행되도록 설정할 수도 있다.

import { useState, useEffect } from 'react';

export function Data() {
  const [data, setData] = useState(null);

  useEffect(() => {
    API.getData()
      .then((response) => { setData(response) });
  }, []);

  const isLoading = (data == null);
  if (isLoading) {
    return 'Loading..';
  }
  return data;
}

위 예제는 useEffect의 inputs에 빈 배열을 넘겨서 최초(componentDidMount)에만 실행되도록 했다. useEffect는 여러 개 사용할 수 있기 때문에 각 state마다 관심사를 분리할 수 있고, 예제처럼 최초에 실행되는 것만 정의해주어도 된다.

그럼 componentWillUnmount는 어떻게 실행할까

useEffect(() => {
  window.addEventListener("mousemove", logMousePosition);
  return () => {
    window.removeEventListener("mousemove", logMousePosition);
  };
}, []);

effect 함수의 return 값이 있는 경우 hook의 cleanup 함수로 인식하고 다음 effect가 실행되기 전에 실행해준다.

componentWillUnmount는 컴포넌트가 사라지기 전에 한 번만 실행했지만, cleanup 함수는 새로운 effect 실행 전에 매번 호출된다는 차이가 있다.

위 예제코드에서는 inputs로 빈 배열을 넘겨주었기 때문에 unmount 될 때 한 번만 실행된다.

🌳 Hooks를 사용했을 때 얻는 이점

  • Functional Component로 통일
  • Custom Hooks: 보다 쉬운 상태 로직 재사용
  • useEffect: 라이프 사이클 API에 흩어져있던 로직을 묶음

Hooks는 HOC나 render-props 같은 패턴이 가져오는 Component Tree의 불필요한 중첩을 없애줄 수 있고 복잡한 패턴을 적용하지 않고 보다 직관적으로 로직을 재사용할 수 있다.

뿐만 아니라 그간 함수형과 클래스형 두 가지 타입(상태가 있는 경우는 클래스형 컴포넌트로, 뷰만 관리하는 경우는 함수형 컴포넌트로 개발하는 등)을 오가면서 개발했던 것을 함수형 컴포넌트로 통일할 수 있다.

클래스형 컴포넌트에서 componentDidMount와 componentWillUnmount에 흩어져있던 관련 코드도 state 마다 묶을 수 있기 때문에 좀 더 연관성 있는 코드끼리 모을 수 있다.

Reference

좋은 웹페이지 즐겨찾기