리액트 컴포넌트를 관리하고 꿰뚫어 보는 것은 매우 어려운 일이다. 공부를 하며 가장 어렵다고 느껴지는 부분은 새로운 것을 학습할 때가 아니라 그 자체에 대한 기본을 갈고 닦는 것 같다.
리액트 훅을 이해하기 위해선 기본적으로 생명주기를 알아야 한다. 기본적으로 useEffect
는 컴포넌트가 랜더링 된 후 어떤 일을 수행하는지 관리하는 역할을 합니다.
컴포넌트가 처음 실행 될 때를 mount
라고 합니다. class 컴포넌트 방식으로 리액트를 구성할 때는 특정 시점마다 설정을 줘야했지만 함수현 컴포넌트의 훅이 나오면서 복잡한 과정을 사용자가 쉽게 조정할 수 있게 바뀌었습니다.
그래도 리액트의 생명주기를 설명하기 위해서는 각각의 단계를 알아야 합니다.
컴포넌트가 처음 호출 되었을 때, context
, props
, state
가 저장이 되고 componentWillMount
가 호출됩니다. 컴포넌트가 mount
되기 바로 직전의 상태를 의미합니다. 여기서 주의할 점은 컴포넌트를 랜더 시키기 위한 상태이기 때문에 props나 state를 바꾸면 컴포넌트를 랜더 시킬 수 없습니다.
componentWillMount
가 호출 되면 컴포넌트가 랜더링 되고 componentDidMount
가 호출됩니다. 리액트 컴포넌트가 랜더링 된 직후를 의미합니다.
만약에 랜더링 된 컴포넌트의 값을 바구고 싶을 때 새롭게 컴포넌트를 생성해서 다시 mount를 한다면 리액트는 지금의 리액트가 될 수 있었을까요? 애초에 이 질문은 리액트의 본질을 무시하는 질문입니다..ㅎ
해당 컴포넌트의 값을 변경해서 리액트 페이지에서 해당 컴포넌트를 새롭게 업데이트 하는 상태입니다.
업데이트가 일어나기 전 값이 변경 되었음을을 감지하고, 여러 메소드를 호출하고 업데이트를 완료합니다. 새롭게 바뀐 컴포넌트가 랜더링이 완료되면 그 상활을 componentDidUpdate
라고 합니다.
마지막으로 컴포넌트가 제거되는 상태가 componentWillUnmount
입니다.
리액트의 생명주기는 이렇게 4가지 과정을 반복합니다.
매번 상태를 관리할 때 모든 과정을써줘야 하는 번거로음을 함수형 컴포넌트의 hook이 완전히 대체하게 됩니다.
useEffect는 엄청난 단순화로 리액트에 혁명을 주게 됩니다.
useEffect(() => {
}, [])
기본적인 구조는 인자로 콜백함수
와, dependency
를 받아옵니다. dependency
를 받지 않는 경우도 있습니다.
dependency
가 없이 콜백함수만 있다면 컴포넌트가 랜더링 될 때마다 실행하는 것을의미하고, 배열 형태로 받아온 dependency
의 state가 업데이트 될 때와 처음 컴포넌트가 랜더링 됐을때 실행됩니다. 만약에 빈배열을 받아온다면 처음 컴포넌트가 생성 됐을 때만 실행하는 함수 입니다.
컴포넌트의 특정 값이 변경 되었을 때 컴포넌트를 업데이트하고, 처음 생성 됐을 때를 처리하는데 마지막 과정이 빠졌습니다.
맞습니다. componentWillUnmount
을 처리하기 위해 Clean Up
과정을 처리해야합니다.
예를 들면 타이머를 동작했다면, 타이머를 종료하는 코드를 작성해야 합니다.
useEffect(() => {
rreturn () => {}
}, [])
함수 안에서 함수를 리턴해 주면 해당 함수를 실행하며 종료를 합니다.
정말 놀랍게 4가지의 메서드를 하나의 형태로 정리가 가능해졌습니다.
예시 코드를 보며 어떻게 쓰이는지 확인해봅시다!
import React, {useState, useEffect} from 'react'
import Timer './TImer'
const App = () => {
const [showTimer, setshowTimer] = useState(false)
return (
<div>
{showTimer && <Timer/>}
<button onClick=(() => setShowTimer(!showTimer))>toggle</button>
</div>
)
}
export default App
---------------------
export default const Timer = () => {
usseEffect(() => {
const timer = setInterval(() => {
console.log('타이머가 작동 중')
}, 1000)
// clean up
return () => {
clearInterval(timer)
}
}, [])
// 처음 컴포넌트가 랜더링 되었을 때만
}
useEffect는 정말 필요한 함수이다. 하지만 많이 사용하면 랜더링 시 컴포넌트 최적화에 무리를 주게 된다.
자식 컴포넌트에 useEffect가 있을 경우 부모 컴포넌트가 다시 랜더링 될 때 계속 호출 되기 때문이다. 그래서 최적화를 위해 리액트 구조를 present, container 구조로 적절하게 짜야한다.
이 부분이 가장 어렵다. 솔직하게 아직 리액트 패턴에 대한 이해가 많이 부족하다. useEffect 대신 useCallback을 사용해서 리랜더링을 막을 수 있지만 이거는 주먹구구식에 불과하다.
최적화를 위해 useEffect를 줄이는게 아니라, 얼마나 좋은 패턴을 가진 리액트 앱을 만들었는지 집중해야 한다.
얼른 리액트 패턴 공부하자!!
마운트 단계까지는 몰랐는데..! 좋은 정보 감사합니다^^