[React] 리액트의 생명주기와 Hook

SuKong·2021년 4월 30일
26
post-thumbnail

리액트의 생명주기

모든 리액트 컴포넌트에는 라이프사이클이 존재한다.
컴포넌트는 ‘생성(mounting) -> 업데이트(updating) -> 제거(unmounting)’ 의 생명주기를 갖는다.
생명주기의 때에 따라 어떤 작업을 처리해야 하는지 지정해줘야 불필요한 업데이트를 방지할 수 있다.
리액트의 클래스 컴포넌트는 라이프사이클 메서드를 활용하고,
함수형 컴포넌트는 Hook을 사용한다.
컴포넌트의 라이프 사이클과 함수형컴포넌트에서 활용하는 Hook중 useEffect에 대해서 알아보자.

✅ 생명 주기 메소드

리액트의 클래스 컴포넌트에서 활용하는 메소드이다.
마운트 -> 업데이트 -> 언마운트 분류에 따른 각각의 생명 주기 메소드는 다음과 같이 존재한다.

1. 마운트 ( 생성 )

컴포넌트의 인스턴스가 생성되어, DOM에 삽입될 때 순서대로 호출된다.

  1. constructor() :
    컴포넌트를 새로 만들 때마다 호출되는 클래스 생성자 메서드
    this.props, this.state에 접근할 수 있으며 리액트 요소를 반환한다.
    setState를 사용할 수 없으며 DOM에 접근해선 안된다.

  2. getDerivedStateFromProps() :
    props에 있는 값을 state에 동기화 시킬 때 사용하는 메서드

  3. render() :
    UI를 렌더링하는 메서드

  4. componentDidMount() :
    컴포넌트가 웹 브라우저 상에 나타난 후 즉 첫 렌더링을 마친 후에 호출하는 메서드
    라이브러리나 프레임워크의 함수를 호출하거나 이벤트 등록, setTimeout, setInterval과 같은 비동기 작업을 처리하면 되고, setState 호출도 이 메서드에서 호출하는 경우가 많다.

2. 업데이트

props나 state가 변경되면 렌더가 진행되며 순서대로 호출된다.

  1. getDerivedStateFromProps() :
    이 메서드는 마운트 과정에서 호출되며, 업데이트가 시작하기 전에도 호출된다.
    props의 변화에 따라 state 값에도 변화를 주고 싶은 경우에 사용한다.

  2. shouldComponentUpdate() :
    props또는 state를 변경했을 때, 리렌더링을 시작할지 여부를 지정하는 메서드
    true를 반환하면 다음 라이프사이클 메서드를 계속 실행하고,
    false를 반환하면 작업을 중지한다.

  3. render() :
    컴포넌트 리렌더링  

  4. getSnapshotBeforeUpdate() :
    컴포넌트 변화를 DOM에 반영하기 바로 직전에 호출하는 메서드

  5. componentDidUpdate() :
    컴포넌트 업데이트 작업이 끝난 후 호출하는 메서드.

3. 언마운트 ( 마운트 해제 )

컴포넌트를 DOM에서 제거하는 과정

  1. componentWillUnmount() :
    컴포넌트를 DOM에서 제거할 때 실행한다.
    이후에 컴포넌트는 다시 렌더링 되지 않으므로, 여기에서 setState()를 호출하면 안된다.

✅ React Hook

리액트의 Hook 은 함수형 컴포넌트에서 React state와 생명주기 기능을 “연동(hook into)“할 수 있게 해주는 함수이다. Hook은 class 안에서는 동작하지 않고, class 없이 React를 사용할 수 있게 해준다.

✨ 리액트 훅을 도입한 목적

리액트 훅을 도입하게 된 목적은 여러가지가 있다.

  1. 컴포넌트에서 상태관련 로직을 사용할 때 Hook 이전에 재사용 가능한 로직을 사용하기 위해서는, render props나 고차 컴포넌트와 같은 패턴을 사용했는데, 이런 패턴은 코드의 추적을 어렵게 만들었다.
    Hook을 활용하면 상태 관련 로직을 추상화해 독립적인 테스트와 재사용이 가능해 레이어 변화 없이 재사용할 수 있다.

  2. 두 번째 목적은 기존의 라이프사이클 메서드 기반이 아닌 로직 기반으로 나눌 수 있어서 컴포넌트를 함수 단위로 잘게 쪼갤 수 있다는 이점 때문이다.
    (라이프사이클 메서드에는 관련 없는 로직이 자주 섞여 들어가는데, 이로인해 버그가 쉽게 발생하고, 무결성을 쉽게 해친다.)

그 외에도 클래스 기반 컴포넌트를 지양하고자 하는 목적 등도 있다.

✨ Hook 사용 규칙 두가지

1️⃣ 최상위에서만 Hook을 호출해야한다. ( 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하면 안된다. )
이 규칙을 따르면 컴포넌트가 렌더링될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장된다.

2️⃣ 리액트 함수 컴포넌트에서만 Hook을 호출해야하고, 일반 JS함수에서는 Hook을 호출해서는 안된다.

이 두가지 규칙을 강제하는 eslint-plugin-react-hooks 라는 ESLink플러그인이 있는데, 이 플러그인은 Create React App에 기본적으로 포함되어 있다.

✨ Hook 종류

📌 기본 Hook

  1. useState
  2. useEffect
  3. useContext

📌 추가 Hooks

  1. useReducer
  2. useCallback
  3. useMemo
  4. useRef
  5. useImperativeHandle
  6. useLayoutEffect
  7. useDebugValue

hook의 종류는 위와 같다.
그중 기본적으로 활용되는 useState와 useEffect를 알아보겠다.

1. useState

동적으로 바뀌는 값을 관리할 때 사용 하며,
상태 유지 값그 값을 갱신하는 함수를 반환한다.

const [state, setState] = useState(initialState);

최초로 렌더링을 하는 동안, 반환된 state는 첫번째 전달된 인자( initialState ) 의 값과 같다.

setState함수는 state를 갱신할 때 사용한다.

setState( newState );

다음 리렌더링 시에 useState를 통해 반환받은 첫 번째 값은 항상 개신된 최신 state가 된다. 즉, initialState인자는 초기 렌더링 시에만 사용되며 이후의 렌더링 시에는 이 값은 무시된다.

사용 예 ).

import React, { useState } from 'react'
export default function Profile () {
    const [name,setName] = useState('');
    return (
        <div>
            <p>{`name is ${name}`}</p>
            <input type='text' value={name} onChange={e=>setName(e.target.value)}></input>
        </div>
    )
}

2. useEffect

리액트 컴포넌트가 렌더링 될때마다 특정 작업 ( effect )을 실행할 수 있도록 하는 hook
component가 mount, update, unmount됐을 때 작업을 처리할 수 있다.

또한 hook이 클래스 컴포넌트의 기능을 사용하기 위해 만들어진 것이므로,
useEffect는 클래스 컴포넌트에서 사용하는 생명 주기 메소드의 네가지 메소드의 기능을 모두 수행한다.
componentDidMount, componentDidUpdate, componentWillUnmount, getDerivedStateFromProps 가 그 네가지의 메소드이다.

useEffect는 useEffect(callBackFunc, dependencies) 로 두개의 인자를 넣어 호출할 수 있다.

1️⃣ useEffect(callBackFunc);
2️⃣ useEffect(callBackFunc, []);
3️⃣ useEffect(callBackFunc, [state1, state2]);
4️⃣ useEffect(()=>{ return(() => func()) });

위의 네가지 형태로 useEffect를 호출할 수 있다.
1️⃣의 경우 렌더링이 될 때마다 (컴포넌트가 마운트 된 후, 컴포넌트가 업데이트되고 난 후, 컴포넌트가 언마운트 되기 전에) 실행이된다.
즉, 앞서 말한 생명주기 메소드의 네가지의 메소드를 모두 사용한 것과 마찬가지이다.

2️⃣의 경우 컴포넌트가 최초 렌더링 되었을 때만 실행이 된다.
즉, 생명주기 메소드의 ComponentDidMount 의 역할을 수행한다.

3️⃣의 경우 최초 렌더링 + state1 또는 state2가 변경되었을 때 실행이 된다.
즉, 생명주기 메소드의 componentDidUpdate, getDerivedStateFromProps 의 역할을 수행한다.

4️⃣useEffect는 clean-up이라고 표현하는 함수를 return할 수 있는데, clean-up함수(리턴하는 함수)를 활용해 컴포넌트가 Unmount될 때 정리하거나 unscribe 해야할 것을 처리한다.
즉, clean-up함수는 생명주기 메소드의 componentWillUnmount의 역할을 수행한다.
예를들어

useEffect(()=>{
	console.log("로그로그");     
    return(() => exampleAPI.unsubscribe());
})

위의 경우 컴포넌트가 렌더링될 때마다 console.log("로그로그")를 수행하고,
컴포넌트가 unmount될 때 console.log("로그로그")exampleAPI.unsubscribe()를 수행한다.

useEffect() 사용 예.

import React, { useState, useEffect } from 'react'
function MyComponent() {
    const [count, setCount] = useState(0);
    useEffect(()=>{
        document.title = `업데이트 횟수 : ${count}`
    })
    return <button onClick={()=>setCount(count+1)}>increase</button>
}

버튼을 클릭하면 count의 상태값이 1씩 증가하는 코드이다.
버튼을 클릭하면 다시 렌더링되고, useEffect훅에 입력된 함수가 호출된다.

❗️❗️effect 타이밍

useEffect로 전달된 함수는 컴포넌트 렌더링 - 화면 업데이트 - useEffect실행 순으로 실행이 된다.
즉, useEffect실행이 최초 렌더링 이후 에 된다는 것을 유의하자.

만약 화면을 다 그리기 이전에 동기화 되어야 하는 경우에는, useLayoutEffect()를 활용하여 컴포넌트 렌더링 - useLayoutEffect 실행 - 화면 업데이트 순으로 effect를 실행시킬 수 있다.


참고 링크

profile
안녕하세요 🤗

3개의 댓글

comment-user-thumbnail
2021년 4월 30일

너무 이해하기 쉽게 주요 개념들 깔끔하게 정리해주셔서 좋았어요 :) 감사합니다!!

답글 달기
comment-user-thumbnail
2023년 5월 24일

관련 강의를 듣다 이해가 되지 않아 구글링 했는데 많은 도움이 되었습니다! 혹시 출처 남기고 링크 걸어도 될까요?

답글 달기
comment-user-thumbnail
2024년 1월 20일

Getting success in the career-oriented certifications exam such as Salesforce PDI is a hard nut to crack. Dumpsaway plays a vital role in materializing your dreams into success by getting through the Salesforce PDI exam. Dumpsaway offers multiple products to suit your preparatory needs.
PDI exam questions answers

답글 달기