React에는 4가지의 Built-in Hooks
Hooks란?
함수 컴포넌트에서 React state &생명주기 기능(lifecycle features)을 “연동(hook into)“ 할 수 있게 해주는 함수
class 내부에서 동작x
→ class 없이 React를 사용 가능하게 함
Hook을 직접 만드는 것(custom hook)도 가능
→ 컴포넌트 간에 상태 관련 로직을 재사용하기 위해
:: 공식 문서
- 컴포넌트 사이에서 상태 관련 로직의 재사용 힘듦.
- 복잡한 컴포넌트 → 이해 힘듦
- Class: 사람과 기계 혼동 시킴
:: 클래스 → 클로저(Closure)
- 클래스 문제
- state가 참조하는 값: 항상 최신이기 때문에 결과가 보장되지 않는다.
(ex. 인☆그램 follow를 하고 빠르게 다른 사람 follow를 하면 마지막 follow한 사람 것만 알림창이 뜬다.)
⇒ 해결: 함수 컴포넌트에 클로저(state, 라이프사이클 구현)
import React, { useState } from "react"
function Hooks(props) {
if (!props.isExist) {
const [state, setState] = useState(); // Error!
}
const [state2, setState2] = useState(); // Error!
}
// react가 여러 훅들을 구분할 수 있는 유일한 정보는 훅이 사용된 순서 뿐이기 때문.
const [state, setState] = useState(initialState);
state
) === 첫 번째 전달된 인자(initialState
)의 값setState
함수 setState(newState + 1); // setState와 동일하게 비동기 업데이트
setState(prev => prev + 1);
useState
를 통해 반환 받은 첫 번째 값은 항상 갱신된 최신 state우선, 사용법 숙지
- (기존 setState와 마찬가지로) 비동기 update
- useState 실행 시 state, setState 한 쌍이 부여됨
기억해 둘 개념
- 가비지 컬렉팅
- 함수는 실행이 완료되면 함수 내에서 사용했던 모든 메모리들을 정리
- 더 이상 참조하는 곳이 없는 메모리에 대해서 실행
- 클로저
- 함수 실행이 끝나고도 메모리에 스스로를 남겨둘 수 있는 방법
- 자신이 생성될 시점의 환경을 기억하는 함수
// 아주 간단한 버전의 useState
const useState = (init = undefined) => {
let value = init
const getter = () => value // 클로저
const setter = next => (value = next) // 클로저
return [getter, setter]
}
const [state, setState] = useState('클로저')
state()
setState("어려워!")
state()
useState()
는 상태 분할 관리가 좋다
어떻게 useState
는 상태값 저장하나?
함수는 상태 저장 x
클로저가 출동한다면?
- 함수형 컴포넌트: 상태 관리 가능(Hooks 사용→ state 저장 시, useState()
사용)
useState()
const useState = (init = undefined) => {
let value = init
const getter = () => value // 클로저
const setter = next => (value = next) // 클로저
return [getter, setter]
}
const [state, setState] = useState('클로저')
- 두 함수 (`getter()`, `setter()` ): 배열 형태로 리턴
- `useState`사용 시, 배열 구조분해 할당 형태 사용 다수
→ 일반적인 함수
실행 종료(return)되고 나서 내부의 데이터들이 가비지 컬렉팅 되어야 됨
→ 이 경우
내부 함수: 지역 변수 참조 → 안 사라짐
외부로 노출된 getter, setter 함수를 통해 내부 변수에 지속적으로 접근하며 호출/재할당 가능
setState
비동기 처리(두 번째 인자) 방법 // class component
handleBtnColor = () => {
this.setState({
color: "red"
}, () => console.log(this.state.color))
}
// function component
const [color, setColor] = useState("blue")
const handleBtnColor = () => {
setColor("red")
}
useEffect(() => {
console.log(color)
}, [color])
- componentDidMount/ componentDidUpdate/ componentWillUnmount와 같은 목적 + 하나의 API로 통합
useEffect(function);
useEffect(() => {}, [count]) // 의존성 배열 (배열 안에 담긴 값들을 추적, 그때 마다 업데이트)
인자: 명령형 or 어떤 effect를 발생하는 함수
(useEffect
에 전달된) 함수 수행 시기
- 화면에 렌더링이 완료된 후 수행
- 기본적으로 동작: 모든 렌더링 완료 후에 수행
- 어떤 값이 변경되었을 때만 실행 되게 할 수 있음
사용법
useEffect(() => {
console.log("componentDidUpdate")
})
useEffect(() => {
console.log("componentDidMount")
return () => console.log("componentWillUnmount")
}, [])
useEffect(() => {
console.log("componentDidMount")
console.log("componentDidUpdate") // + shouldComponentUpdate
return () => console.log("componentWillUnmount")
}, [state])
// WRONG!!
useEffect(() => {
console.log("CDM 쓰고 싶어요")
}, [])
useEffect(() => {
console.log("CDU 쓰고 싶어요")
}, [state])
서로 관련 없는 로직들: 여러 useEffect
사용 해서 갈라 놓기
useEffect
역할
컴포넌트가 렌더링 이후에 어떤 일을 수행 해야 하는 지 알려줌
→ 우리가 넘긴 함수를 기억했다가(이 함수를 ‘effect’라고 부릅니다) DOM 업데이트를 수행한 이후에 불러낼 것
useEffect
는 렌더링 후 매번 수행 (수정 가능)
렌더링 이후에 발생하는 것으로 생각하라
effect가 수행되는 시점
이미 DOM이 업데이트되었음을 보장
기본: 첫번째 렌더링과 이후 모든 업데이트에서 수행
useEffect vs 라이프 사이클
useEffect: 여러가지 라이프 사이클이 합쳐진 형태
- componentDidMount (의존성 배열 : [])
- shouldComponentUpdate (의존성 배열 내에 해당 사항 없을 경우)
- componentDidUpdate (의존성 배열 자체가 없거나, 해당 사항이 있을 경우)
- componentWillUnmount
Render → Effect Callback → Clean Up!
const Foo = () => {
const [state, setState] = useState(0);
console.log("render", state);
useEffect(() => {
console.log("useEffect Callback", state);
return () => console.log("cleanUp", state);
}, [state]);
return <div onClick={() => setState(state + 1)}>하잉</div>;
};
export default Foo;
- 함수 body ⇒ `render`
- useEffect
- 의존성 배열 : useEffect 내부에서 해당 값의 변화만(!) 감지
- 클린업 함수 : 구독/해제 등 다음 effect 전에 이번 effect의 내용을 정리해야 할 때 사용
render, 0 useEffect Callback, 0 // 클릭 render, 1 cleanUp, 0 useEffect Callback, 1
- 클린업 함수가 (다음 번 render 보다) 먼저 실행하는 이유
- 렌더 완료 후 → effect 실행
- 더 나은 ux 제공
- effect 실행 시점에 렌더 완료 보장 가능
- 클린업 함수 값: 예전 사이클 값 가져오는 이유
- 클린업 함수가 클로저("자신이 생성될 시점의 환경을 기억하는 함수")로서 생성될 시점: 이전 렌더 때, 즉 이전 환경을 기억함.