본문 바로가기
  • Monstera
개발공부/React

[React] 리액트 Hooks의 useState와 useEffect

by 네모공장장 2020. 3. 18.

/ React Hooks 란?

리액트 v16.8로 업데이트 되면서 함수형 컴포넌트와 Hooks 사용을 권장하고 있다.

Hook 함수형컴포넌트에서 React state와 생명주기 기능(lifecycle features)을 연동(hook into)할 수 있게 해주는 함수이다. 클래스형 컴포넌트 안에서는 동작하지 않지만 클래스형 컴포넌트없이 리액트를 사용할 수 있게 해준다.

 

장점

  • 컴포넌트로부터 상태 관련 로직을 추상화할 수 있다.
  • 생명주기를 사용했을때 일어나는 버그와 무결성 문제를 보완할 수 있다.
  • 클래스형 컴포넌트의 this로 인한 혼동과 코드양을 줄인다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

/ 함수형 컴포넌트

import React from 'react';

const App = () => {
	return(
		<h1>안녕하세요!</h1>
	)
}

export default App;

es6기반 함수형 컴포넌트의 기본 형태이다.

상세설명은 [React] 함수형 컴포넌트 포스팅 참고

/ Hooks

1. useState()

useState 상태값, 메소드가 담긴 배열을 반환하며, 초기값을 설정할 수 있다.

// useState의 반환값을 userState변수에 저장.
const userState = useState(0);

console.log(userState); // 출력값 [0,메소드] = [상태값, 메소드]
  

위의 코드에 기초해 구조분해할당(Destructuring assignment)을 통해 다시 표현하면

[상태값, 메소드] = useState(기본값) 이다. 

state에 해당하는 부분이 배열 첫번째의 상태값이고, setState를 해주는 부분이 배열 두번째 값인 메소드이다.

 

아래는 useState를 사용하여 버튼을 클릭할 때마다 숫자가 1씩 증가하는 예시코드이다.

import React, { useState } from 'react';

const Counter = () => {
  const [count,setCount] = useState(0);
  const countFunction = () => {
    setCount(count+1)
  };


  return(
    <div>
      <p>You Clicked {count} times!</p>
      <button onClick={countFunction}>Click me</button>
    </div>
  )
}

export default Counter;

Click me버튼을 클릭하면 countFunction이 실행된다.

countFunction은 setCount를 이용하여 useState의 count상태를 1증가 시킨다. 

useState에서 반환하는 메서드는 상태를 업데이트한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2. useEffect()

useEffect 컴포넌트에서 side effects를 수행한다.

기존에 React lifecyle 내에서 사용했던 로직을 useEffect를 이용해 구현할 수 있다.

(데이터 fetch, subscription 설정, DOM 수정 등)

clean-up함수가 필요하지 않은 경우와 필요한 경우로 크게 두가지의 경우로 구분할 수 있다.


2-1. clean-up 함수가 필요하지 않은 경우

기본 형태는 useEffect( effect, [상태값1, 상태값2 ...]) 로

두번째 인자에 명시한 상태값이 변할때마다, 즉 랜더링될 때마다 수행된다.

배열을 삭제하거나 null로 두면 모든 상태값이 변화할 때마다 useEffect는 실행된다.

반면 빈 배열을 넣으면 최초랜더링 시에만 useEffect가 실행된다.

lifecyle에 익숙하다면 componentDidMount와 componentDidUpdate에서 실행된다고 생각할수 있겠다.

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

const Counter = () => {
  const [countA,setCountA] = useState(0);
  const setCountAFunction = () => {
    setCountA(countA+1)
  };

  const [countB,setCountB] = useState(0);
  const setCountBFunction = () => {
    setCountB(countB-1)
  };

  // case1 : 최초 랜더링시에만 실행(componentDidMount)
  useEffect(() => {
    console.log(`initial count ${countA}, ${countB}`);
  },[]);

  // case2 : countA의 상태변화에 따라 실행
  useEffect(() => {
    console.log(`countA : ${countA}`);
  },[countA]);

  // case3 : 모든 상태변화에 따라 실행(componentDidMount + componentDidUpdate)
  useEffect(() => {
    console.log(`countA : ${countA} countB : ${countB}`);
  },null);

  return(
    <div>
      <p>Number increase : {countA}</p>
      <button onClick={setCountAFunction}>Click me</button>
      
      <p>Number decrease : {countB}</p>
      <button onClick={setCountBFunction}>Click me</button>
    </div>
  )
}

export default Counter;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2-2. clean-up 함수가 필요한 경우 

기본 형태인 useEffect(effect, [상태값1, 상태값2 ...])에서  effect에 정리가 필요한 경우라면 effect가 함수를 리턴하게하면 된다. (ex.메모리누수방지, 데이터 초기화 등 의 상황 ) 

그리고 그 때 리턴되는 함수를 clean-up함수라고 한다.

effect의 리턴값이 함수라면 리액트는 그 함수를 정리가 필요한 때에 실행시킨다.

(리액트 lifecycle의  componentWillUnmount에 해당한다고 생각하면 된다.)

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

const Counter = () => {
  const [countA,setCountA] = useState(0);
  const setCountAFunction = () => {
    setCountA(countA+1)
  };

  const [countB,setCountB] = useState(0);
  const setCountBFunction = () => {
    setCountB(countB-1)
  };

  useEffect(() =>{
    console.log(`${countA} , ${countB}`);
    console.log('effect',new Date().getMilliseconds())
    // 이하 clean-up함수
    return ()=>{
      console.log(`${countA} , ${countB}`);
      console.log('clean-up',new Date().getMilliseconds())
    }
  },null);

  return(
    <div>
      <p>Number increase : {countA}</p>
      <button onClick={setCountAFunction}>Click me</button>
      
      <p>Number decrease : {countB}</p>
      <button onClick={setCountBFunction}>Click me</button>
    </div>
  )
}

export default Counter;

아래의 이미지는 위 코드의 실행결과인데,

clean-up함수와 effect의 실행 시점을 확인하기위해 콘솔에 milliseconds를 함께 찍어보았다.

 

clean-up 함수가 실행된 후 바로 effect가 발생하는것을 확인할수 있다.

더불어 clean-up은 방금 실행된 effect의 이전 effect 상태를 반영하기때문에 2가 찍혀있고,

effect는 상태변화를 반영했으므로 3이 찍혀있는것을 확인할 수 있다.

 

정확한 시점은 리액트가 다음 차례의 effect를 실행하기 전,

이전의 렌더링에서 파생된 effect를 정리할때 실행되는 것이다. 

 

그럼 기존 lifecycle의 componentDidUpdate 해당하는 것은? 이라고 생각한다면, 

기본적으로 useEffect는 업데이트를 다루기 때문에 그 자체로 충분하다고 알아두자.

 

 

 

     
     

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형