본문 바로가기

Tech/React

프론트 입문 리액트 삽질기 3 - Effect Hook으로 Lifecycle API 동작 구현하기

반응형

Effect Hook, Lifecycle API

클래스형 컴포넌트에서 사용하던 state는 함수형 컴포넌트에서 State Hook으로 대체하여 사용할 수 있었습니다.

 

이제 특정 동작을 수행(?) 했을 때 동작하는 함수를 추가해 보려고 합니다.

기존 클래스형 컴포넌트에서는 Lifecycle API라는 컴포넌트의 생성, 업데이트, 제거 시 호출되는 API를 사용해 브라우저에 컴포넌트가 생성, 업데이트 혹은 삭제되었을 때 특정 액션을 수행하도록 작성할 수 있었습니다.

import React, { Component } from 'react';

class Example extends Component {
    componentDidMount() {
        // 컴포넌트 생성
      }

    componentDidUpdate(prevProps, prevState, snapshot) {
        // 컴포넌트 업데이트
    }
    
    componentWillUnmount() {
        // 컴포넌트가 DOM 상에서 제거
    }
} 

export default Example

 

함수형 컴포넌트에서는 Lifecycle API 대신에 Effect Hook을 사용할 수 있는데, Effect Hook은 다음과 같이 작성합니다.

예시 작성을 위해 이전 글에서 작성한 Greeting 컴포넌트를 가져와 수정했습니다.

import { useEffect } from "react";

function Greeting({name="Not Defined"}){
    useEffect(() =>{
        console.log("Effect Hook Executed!")
    }
    )

    return (
        <div>You entered <b>{name}</b>!</div>
    );
}

export default Greeting;

 

동작을 확인해 보겠습니다.

Effect Hook 동작

컴포넌트가 처음 생성될 때 Effect Hook이 한번 실행되고, 이후 컴포넌트가 업데이트될 때 마다 Effect Hook이 실행되는 것을 확인할 수 있습니다.

즉, 저 useEffect Hook 하나가 componentDidMount와 componentDidUpdate 두 동작을 수행하고 있음을 확인할 수 있습니다.

그럼 두 동작을 분리하려면 어떻게 해야 할까요?

 

useEffect 함수의 내부를 살펴보았습니다.

    /**
     * Accepts a function that contains imperative, possibly effectful code.
     *
     * @param effect Imperative function that can return a cleanup function
     * @param deps If present, effect will only activate if the values in the list change.
     *
     * @version 16.8.0
     * @see https://reactjs.org/docs/hooks-reference.html#useeffect
     */
    function useEffect(effect: EffectCallback, deps?: DependencyList): void;

 

EffectCallback 함수와 DependencyList를 인자로 받는 것을 확인할 수 있었습니다.

리액트 공식 도큐먼트에서는 다음과 같이 설명하고 있습니다.

 

Using the Effect Hook – React

A JavaScript library for building user interfaces

ko.reactjs.org

특정 값들이 리렌더링 시에 변경되지 않는다면 리액트로 하여금 effect를 건너뛰도록 할 수 있습니다.
useEffect의 선택적 인수인 두 번째 인수로 배열을 넘기면 됩니다.

즉, 이 useEffect에 배열을 넘겨주면 해당 배열을 모니터링하며 컴포넌트가 리랜더링 되었을때 리랜더링 전의 값과 리랜더링 후의 값을 비교하여 일치하지 않을 때만 EffectCallBack을 실행하는 구조가 됩니다.

이를 응용하면, 빈 배열 []를 인자로 넣어준다면 componentDidMount와 유사한 동작을, 인자로 값이 변화하는 의존성 배열을 넣어준다면 componentDidUpdate와 유사한 동작을 수행하는 Hook이 될 수 있습니다.

이 때, 빈 배열을 인자로 넣을 경우 effect 외부의 state나 props를 참조하지 않도록 주의하여 작성해야 합니다.

 

Hook 자주 묻는 질문 – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

그리고 Lifecycle API의 componentWillUnmount와 유사한 동작을 수행하기 위해서는 effect 내에서 함수를 반환하면 됩니다.

 

Using the Effect Hook – React

A JavaScript library for building user interfaces

ko.reactjs.org

정리(clean-up)의 실행을 위해 별개의 effect가 필요하다고 생각할 수도 있습니다. 하지만 구독(subscription)의 추가와 제거를 위한 코드는 결합도가 높기 때문에 useEffect는 이를 함께 다루도록 고안되었습니다. effect가 함수를 반환하면 리액트는 그 함수를 정리가 필요한 때에 실행시킬 것입니다.
effect에서 함수를 반환하는 이유는 무엇일까요?
이는 effect를 위한 추가적인 정리(clean-up) 메커니즘입니다. 모든 effect는 정리를 위한 함수를 반환할 수 있습니다.
리액트가 effect를 정리(clean-up)하는 시점은 정확히 언제일까요?
리액트는 컴포넌트가 마운트 해제되는 때에 정리(clean-up)를 실행합니다. 하지만 위의 예시에서 보았듯이 effect는 한번이 아니라 렌더링이 실행되는 때마다 실행됩니다. 리액트가 다음 차례의 effect를 실행하기 전에 이전의 렌더링에서 파생된 effect 또한 정리하는 이유가 바로 이 때문입니다.

 

아직 컴포넌트를 동적으로 생성하고 제거하는 방법은 익히지 못해서 componentDidMount, componentDidUpdate 두 Lifecycle API 동작만을 Hook으로 구현해보기 위해 Counter 컴포넌트를 새로 만들었습니다.

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

function Counter(){
    const [cnt, setCnt] = useState(0)

    useEffect(()=>{
        console.log("Counter Spawned!")
    }, [])

    useEffect(()=>{
        console.log("Count now "+cnt)
    }, [cnt])

    return (
        <Fragment>
          <div>Count = {cnt}</div>
          <button onClick={()=>setCnt(cnt-1)}> - </button>
          <button onClick={()=>setCnt(cnt+1)}> + </button>
        </Fragment>
        );
}

export default Counter;

 

변경한 App.js는 다음과 같습니다.

import { Fragment } from 'react';
import './App.css';
import Counter from './Counter';

function App() {
  return (
    <Fragment>
      <h1>Effect Hook Example - Counter</h1>
      <Counter/>
    </Fragment>
    );
}

export default App;

 

이 코드는 다음과 같이 동작합니다.

Effect Hook 동작

 

 

반응형