[React] Hooks - useState & useEffect

심언조·2021년 4월 1일
5

React

목록 보기
1/2
post-thumbnail

📕 React Hook 이 만들어진 배경

  • 복잡한 컴포넌트는 이해하기 어렵다.
  • class 의 this 키워드는 혼란을 야기한다.
  • class 없이 function 에서 React 기능들을 사용할 수 있게 한다.

Hook은 React 16.8에 새로 추가된 기능입니다. Hook은 class를 작성하지 않고도 state와 다른 React의 기능들을 사용할 수 있게 해줍니다.

Hook을 사용함으로써 function에서도 class에서 사용하던 상태관리생명주기를 제어할 수 있게 되었다. 또한 코드를 간결하고 직관적으로 작성할 수 있는 장점까지 갖췄다니 안써볼 수 없겠다.

여러가지 Hook이 있으나 가장 기본적인 useState, useEffect에 대해서 알아보도록 한다.

📙 useState

리액트에서 가장 흔하게 사용되는 counter 예제를 통해 살펴보자 먼저 클래스를 사용해서 작성한다면 다음과 같이 될 것이다.

class Counter extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
    
    this.handleCountPlus = this.handleCountPlus.bind(this);
    this.handleCountMinus = this.handleCountMinus.bind(this);
  }
  
  handleCountPlus(){
    this.setState({
      count:this.state.count + 1
    });
  }
  
  handleCountMinus(){
    this.setState({
    	count:this.state.count + 1
    });
  }
  
  render(){
    return(
      <div>
        <h2>Class Component</h2>
        <p>Count number : {this.state.count}</p>
        <button onClick={this.handleCountPlus}>Plus</button>
        <button onClick={this.handleCountMinus}>Minus</button>
      </div>
    );
  }
}

위 코드에서 state{count:0} 이고 각 버튼 클릭이벤트에서 this.setState()를 사용하여 count 상태를 변경한다. 그리고 이제 useState 를 사용한 아래 코드를 보자.

import React, {useState} from 'react';

function Counter(){
  const [count, setCount] = useState(0);
  
  const handleCountPlus = () =>{
    setCount(count+1);
  }
  
  const handleCountMinus = () =>{
    setCount(count-1);
  }
  
  return (
     <div>
       <h2>Function Component</h2>
       <p>Count number : {count}</p>
       <button onClick={handleCountPlus}>Plus</button>
       <button onClick={handleCountMinus}>Minus</button>
     </div>
  );
}

위 코드는 클래스로 만든 코드와 동일하게 동작한다. 클래스 컴포넌트에 비해 코드의 양이 많이 줄고 this또한 사용하지 않으니 코드가 매우 직관적으로 보인다. 이제 어떻게 useState를 사용하는지 살펴보자.

import React, {useState} from 'react';

먼저 useState를 사용해주기 위해 리액트 패키지에서 제공하는 useState를 불러온다.

const [count, setCount] = useState(0);

그 다음 useState(0) 에 넣은 인자값 0을 초기값으로 지정하고 반환된 첫번째 값은 상태(count) 두번째 값은 상태를 변경할시 호출하는 함수(setCount, setter 함수라고 부른다)이다.

const countState = useState(0);
let count = countState[0];
let setCount = countState[1];

위 코드와 같이 사용할 수도 있지만 구조분해 할당을 통해 간단하게 사용할 수 있다.

const handleCountPlus = () =>{
  setCount(count+1);
}

const handleCountMinus = () =>{
  setCount(count-1);
}

return (
     <div>
       <h2>Function Component</h2>
       <p>Count number : {count}</p>
       <button onClick={handleCountPlus}>Plus</button>
       <button onClick={handleCountMinus}>Minus</button>
     </div>
);

그리고 각 버튼 클릭이벤트에 setCount를 사용하여 인자로 받은 값을 최신상태로 변경해 준다.

위에서 작성한 코드 예제의 실행 결과는 아래에서 확인할 수 있다.

📘 useEffect

이제 useEffect를 사용하여 컴포넌트가 마운트 됐을 때, 언마운트 됐을 때 그리고 업데이트 되었을 때에 특정한 작업을 하는 방법을 알아보자. 이번에는 시계 컴포넌트를 예로들어 코드를 작성한다.

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

function Clock(){
  const [datetime, setDatetime] = useState(new Date());
  const [tickCount, setTickCount] = useState(0);
  
  useEffect(()=>{
    console.log("화면에 나타남(Did mount)");
    const intervalId = setInterval(() => {
      setDatetime(new Date());
    }, 1000);

    return () => {
      console.log("화면에서 사라짐(Will Unmount)");
      clearInterval(intervalId);
    };
  },[]);
  
  useEffect(()=>{
    console.log("상태가 갱신됨(Did update)");
    setTickCount(tickCount + 1);
  }, [datetime]);
  
  return (
    <div>
      <h2>Now Datetime is : {datetime.toLocaleTimeString()}</h2>
      <h2>Tick Count : {tickCount}</h2>
    </div>
  );
}

위 예제 코드는 컴포넌트가 마운트됐을 때 setInterval 을 사용하여 매 초마다 datetime 상태를 갱신 시키다가 언마운트 될 때 clearInterval을 사용하여 매초마다 실행시키던 함수를 종료한다. 이제 useEffect 사용방법에 대해 살펴보자.

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

useEffect를 사용하기 위해 리액트 패키지에서 제공하는 useEffect를 불러온다.

useEffect(()=>{
    console.log("화면에 나타남(Did mount)");
    // Do something...
  
    return () => {
      console.log("화면에서 사라짐(Will unmount)");
      // cleanup function
      // Do something... 
    };
},[]);

useEffect 의 첫번째 인자는 실행시킬 함수, 두번째 인자는 의존값이 들어있는 배열을 줄 수 있다. 만약 두번째 인자값을 빈 배열로 사용한다면, 컴포넌트가 처음나타날 때(Did mount) 첫번째 인자로 넘겨준 함수가 호출되고, return 한 함수에서는 컴포넌트가 사라질 때(Will unmount) 실행된다. 이를 cleanup 함수라고 부른다.

주로 마운트 될 때 하는 작업은 다음과 같은 사항들이 있다.

  • API 요청(AJAX)
  • 라이브러리 사용
  • setInterval 을 통한 반복 작업 혹은 setTimeout을 통한 작업 예약

그리고 언마운트 할 때 하는 작업은 다음과 같다.

  • 라이브러리 인스턴스 제거
  • setInterval, setTimeout 을 사용하여 등록한 작업들 clear 하기 (clearInterval, clearTimeout)
useEffect(()=>{
  console.log("상태가 갱신됨(Did update)");
  // 특정 상태가 변했을때 호출할 함수 내용 작성
  
  return ()=>{
    console.log("상태가 바뀌기 전 호출 Will update)");
  };
},[state]);

그리고 위와 같이 useEffect의 두번째 인자에 특정값을 넣었을 때에는 컴포넌트가 마운트 될 때와 지정한 값이 바뀔 때 호출이 된다. 그리고 return에 함수를 작성했다면 컴포넌트가 언마운트 될 때와 지정한 값이 바뀌기전의 상태에서 함수를 실행할 수 있다. 두번째 인자를 생략하면 컴포넌트가 리렌더링(render)될 때마다 호출된다.

아래는 예제코드를 약간 변경하여 체크박스의 체크여부에 따라서 useEffect 가 어떻게 동작하는지 알기 편하도록 했다.

⚠️ 유의할 점

Hook 사용할 때 두가지 규칙을 준수해야 한다. 이러한 규칙을 강제하기 위해서 제공하는 linter 플러그인도 제공한다고 하니 관심이 있다면 알아보도록 하자.

최상위에서만 Hook을 호출할 것

반복문이나 조건문 또는 중첩된 함수에서 Hook을 호출하면 안된다. 위 규칙을 따르지 않으면 컴포넌트가 제대로 작동하지 않을 수 있다.

Hook은 "함수"컴포넌트에서만 작동한다.

또 한가지 유념할 점은 Hook 은 클래스 안에서는 동작하지 않는다는 점이다. 바꿔 말하면 함수형 컴포넌트에서만 Hook을 사용할 수 있다.

✏️ 정리

  • Hook을 사용함으로써 클래스 컴포넌트를 작성할 때 보다 코드를 간결하고 직관성있게 작성할 수 있다.
  • useState, useEffect 로 함수 컴포넌트에서도 상태 및 생명주기 관리를 할 수 있다.
  • Hook에서는 지켜야할 두 가지 규칙이 있고, 이를 지켜야 정확한 동작을 보장한다.

참고 자료

React 공식메뉴얼 - Hook 소개
벨로퍼트와 함께하는 모던 리액트

profile
웹 개발자

0개의 댓글