리액트에는 두 가지 형태의 컴포넌트가 있다.!
Class, Functional Component!
요즘엔 함수형 컴포넌트를 많이 쓴다지만 라이프 사이클의 이해를 위해선 클래스형 컴포넌트의 이해도 필요할 거 같다!
클래스형과 함수형의 가장 큰 차이는 여러 가지가 있지만 아무래도
라이프 사이클 메서드의 사용에 있어서가 가장 클 것 같다!
클래스형 컴포넌트에서의 리액트 라이프 사이클입니당
1. Mount - 컴포넌트가 생성되는 시기
constructor
컴포넌트의 생성자 메서드, 컴포넌트가 생성됐을 때 가장 먼저 실행이 되고 props, state 등에 this 키워드를 이용해 접근할 수 있고 리액트 요소를 반환한다.!
getDerivedStateFromProps
props로부터 파생된 state를 가져온다!! props로 받아온 데이터를 state에 넣어줄 떄 사용된다!!
render
컴포넌트를 렌더링 하는 메서드
componentDidMount
컴포넌트가 마운트 됐을 때 호출된다!! (컴포넌트의 첫 렌더링을 마치게 되면 호출되는 메서드)
이 메서드가 호출됐을 때는 화면에 컴포넌트가 그려진 상태이다.
주로 DOM을 다루는 외부 라이브러리 연동이나 해당 컴포넌트에서 사용될 데이터를 요청하는 등의 로직이 구현된다.!2. Update - 컴포넌트가 업데이트 되는 시기
getDerivedStateFromProps
컴포넌트의 props, state가 변경됐을때도 이 친구가 호출된다!
shouldComponentUpdate
컴포넌트가 리렌더링을 할지 말지를 결정하는 메서드이다!
componentDidUpdate
컴포넌트가 업데이트 되고 난 후 호출된다!3. Unmount - 컴포넌트가 화면에서 사라지는 것
componentWillUnmount
컴포넌트가 화면에서 사라지기 직전에 호출된다!
주로 DOM에 등록했던 이벤트리스너나 더이상 필요없는 구독 등을 제거하고 만약setTimeout
과 같은 함수를 등록했었다면 제거하는 로직을 구현한다!
메서드를 사용해 라이프 사이클에서의 로직을 관리할 수 있지만 함수형 컴포넌트는 이러한 메서드가 없습니다..!!
그렇다면 어떻게 컴포넌트의 생성(Mount) 업데이트(Update) 제거(UnMount)에서의 상태를 관리하지??
이를 이용한 장점으로는
useState
여기서 주의할 점!
상태 변수에 Object를 넣게 된다면 오브젝트 내부의 값을 변화시켜도 리렌더가 되지 않는 경우가 있을 수 있다.
그 이유로는 리액트의불변성
을 지키기 위해서!!
자바스크립트 엔진의 메모리 구조에서는 원시 타입 변수와 참조 타입 변수의 참조 값은 콜 스택에 저장하고 참조 타입 변수의 실제 값은 메모리 힙에 저장하게 되는데 리액트에서는 상태 변화의 감지를 콜 스택에서 한다!!
그렇기 때문에 배열에 push 등을 사용해 값에 변화를 주거나 구조분해할당을 하지 않은 채 객체의 값을 변화시키면 메모리 힙에 접근해 실제 값만 변경시켜 참조 값은 변화되지 않기에 상태의 변화를 감지하지 못해 리렌더링이 일어나지 않게 된다!
그렇기에 참조 타입의 Object 상태를 변화시켜 리렌더를 요한다면 setState 시 구조 분해 할당 등을 통해 새 참조 값을 만들 수 있도록 해야한다!
객체의 경우에는 쪼개서 상태로 만들 수 있다면 그렇게 하는 것이 추천된다
또한 setState는 비동기로 작동한다! => state의 값은 setState가 호출되는 시점이 아닌, 해당 로직이 구현된 스코프의 로직이 모두 실행된 이후에 바뀐다!!
setState가 여러 개 있을 수도 있고 변경될때마다 바로바로 렌더링이 되면 성능 상 유리하지 않기에!
const [name, setName] = useState(initialState);
useEffect
componentDidMount
, componentDidUpdate
, componentWillMount
가 합쳐졋다!Unmount
시점을 위해 사용된다!// 렌더링 결과가 실제 DOM에 반영된 후마다 호출
useEffect(() => {});
// 의존성 배열을 비워두면 컴포넌트가 처음 나타날 떄 한 번 호출
useEffect(() => {}, []);
// 의존성 배열에 상태를 넣게 되면 조건부 effect 발생, 의존성이 변경되면 effect는 항상 재생성된다.
useEffect(() => {}, [dependency]);
useLayoutEffect
useEffect의 친구! 기본적으로 useEffect는 Side Effect(부수 효과)를 막기 위해 화면 렌더링이 완료된 후나 상태가 업데이터 되었을 떄 사이드 이펙트를 수행한다!
useEffect의 내용은 컴포넌트가 렌더된 후 layout과 paint가 왼료된 후에 비동기적으로 수행된다! 그렇기에 DOM에 영향을 주는 로직이 있을 경우 사용자는 화면이 재구성됨에 따라 깜빡임을 마주할 수 밖에 없다!
useLayoutEffect는 컴포넌트가 렌더된 후 layout과 paint가 왼료되기 전에 동기적으로 수행되기때문에 DOM을 바꾸는 효과가 있더하더라도 이미 완료된 화면을 보기 때문에 UX 면에서 좋다!
하지만 로직이 복잡하고 오래 걸린다면 화면이 그려지기까지의 시간이 길 수 밖에 없기 때문에 자주 사용되는 훅이지는 않다!
useContext
전역 변수를 위한 훅!! Context API를 통해 만들어진 Context에서 제공하는 값을 가져올 수 있다!
const data = useContext(DataContext);
컴포넌트에서 가장 가까운 <DataContext.Provider>
가 갱신된다면 이 훅은 프로바이더에게 제공된 가장 최신의 데이터를 사용해 렌더러를 트리거한다!
useReducer
const [state, dispatch] = useReducer(reducer, initialArgument, init);
useRef
current
프로퍼티로 변경 가능한 DOM 객체의 ref 객체를 반환한다!const ref = useRef(null);
useMemo
const memo = useMemo(() => doingSomething(a, b), [a, b]);
useCallback
useMemo
, useCallback
두 훅 모두 메모이제이션을 사용해 중복 연산을 줄이고 참조된 값이 변경됐을 때만 등록된 함수를 실행한다는 공통점이 있지만 반환 값에서 차이가 있다!const memoCallback = useCallback(() => {
doingSomething(a, b);
}, [a, b]);
완전 천재!!! 그 자체 🥰