3.1 리액트의 모든 훅 파헤치기
useState
함수형 컴포넌트 내부에서 상태를 정의하고, 해당 상태를 관리할 수 있게 해주는 훅
import { useState } from 'react’;
const [state, setState] = useState(initialState);
- 아무런 값을 넘겨주지 않으면 초기값은
undefined
- 반환값은 배열
- 배열의 첫 번째 원소로
state
값 자체를 사용 - 두번째 원소인
setState
함수를 사용해 해당 state의 값 변경 가능 - 클로저를 사용하여
setState
가 호출된 이후에도 지역변수인state
참조 가능
게으른 초기화(lazy initialization)
useState에 변수 대신 함수를 넘기는 것
// 일반적인 useState사용
// 바로 값을 집어넣음
const [count, setCount] = useState(
Number.parselnt(window.localStorage.getltem(cacheKey))
);
// 게으른 초기화
// 위 코드와의 차이접은 함수를 실행해 값을 반환
const [count, setCount] = useState(() =>
Number.parselnt(window.localStorage.getltem(cacheKey))
);
- useState의 초깃값이 복잡하거나 무거운 연산을 포함하고 있을 때 사용 -공식문서
- 해당 함수는 오로지 state가 처음 만들어질 때만 사용
useEffect
애플리케이션 내 컴포넌트의 여러 값들을 활용하여 동기적으로 부수 효과를 만드는 메커니즘
function Component() {
// ...
useEffect(() => {
// do something
}, [props, state]);
// ...
}
-
첫 번째 인수: 실행할 부수효과가 포함된 함수
-
두 번째 인수: 의존성 배열
-
의존성 배열이 변경될 때 마다 콜백 실행
⇒ state와 props의 변화 속에서 일어나는 렌더링 과정에서 실행되는 부수효과
사용 시 주의할 점
- eslint-disable-line react-hooks/exhaustive-deps 주석은 최대한 자제하라
- useEffect의 첫 번째 인수에 함수명을 부여하라
- 거대한 useEffect를 만들지 마라
- 불필요한 외부 함수를 만들지 마라
클린업 함수의 목적
클린 업: 어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하는 과정
이벤트를 등록하고 지울 때 사용
- 새로운 값을 기반으로 렌더링 뒤에 실행되지만 변경된 값을 읽는 것이 아닌 함수가 정의됐을 당시에 선언된 값을 보고 실행
useMemo
비용이 큰 연산에 대한 결과를 저장 후 저장된 값을 반환
import { useMemo } from ’react’;
const memoizedValue = useMemo(
() => expensiveComputation(a, b), [a, b]);
- 컴포넌트의 성능을 최적화 하는데 사용
- 렌더링 시 의존성 배열의 값이 변경되지 않음 → 함수 재실행 x, 기억해둔 해당 값 반환
- 렌더링 시 의존성 배열의 값이 변경됨 → 첫번째 인수의 함수 실행, 해당 값 반환, 해당 값 저장
UseCallback
특정 함수를 새로 만들지 않고 다시 재사용
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
- 함수의 재생성을 막아 불필요한 리소스 또는 리렌더링을 방지하고 싶을 때 사용
useRef
저장공간 또는 DOM에 접근하기 위한 훅
- setState와의 차이점
- useRef는 반환값인 객체 내부에 있는 current로 값에 접근 또는 변경 가능
- useRef는 그 값이 변하더라도 렌더링 발생 X
function RefComponent() {
const count = useRef(0);
function handleClick() {
count.current += 1;
}
// 버튼을 아무리 눌러도 변경된 count 값이 렌더링되지 않는다
return <button onClick={handleClick}>{count.current}</button>;
}
- 컴포넌트가 렌더링될 때만 생성되며, 컴포넌트 인스턴스가 여러 개라도 각각 별개의 값을 바라봄
- useRef의 최초 기본값은 useRef()로 넘겨받은 인수
useContext
contxt와 해당 context를 함수형 컴포넌트에서 사용할 수 있게 하는 훅
- 다수의 Provider와 useContext 사용 시, 별도 함수로 감싸서 사용하는 것이 좋음
useContext 사용 시 주의 점
-
useContext를 함수형 컴포넌트 내부에서 사용할 때는 컴포넌트 재활용이 어려워진다는 것을 염두해두기
⇒ useContext가 있는 컴포넌트는 Provider와의 의존성을 갖게 되는 셈
useReducer
useState를 대체할 수 있는 함수로 복잡한 상태관리가 필요한 경우 사용
import React, { useReducer } from 'react';
const [state, dispatch] = useReducer(reducer, initialState, init);
- state 값을 변경하는 시나리오를 제한적으로 두고 이에 대한 변경을 빠르게 확인할 수 있게 하는 것
용어 정리
- state
- 컴포넌트에서 사용할 상태
- dispatch 함수
- 첫번째 인자인
reducer
함수를 실행 - 컴포넌트 내에서
state
의 업데이트를 일으키키 위해 사용하는 함수
- 첫번째 인자인
- reducer 함수
- 컴포넌트 외부에서
state
를 업데이트 하는 함수 - 현재
state, action
객체를 인자로 받아, 기존의state
를 대체하여 새로운state
를 반환하는 함수
- 컴포넌트 외부에서
- initialState
- 초기
state
- 초기
- init
- 초기 함수(초기
state
를 조금 지연해서 생성하기 위해 사용)
- 초기 함수(초기
useState와 useReduce의 차이점
-
세부 동작과 쓰임에만 차이가 있음
⇒ 클로저를 활용하여 state를 관리하는 것은 같음
useImperativeHandle
부모에게서 넘겨받은 ref를 원하는 대로 수정할 수 있는 훅
useLayoutEffect
useEffect와 동일하지만, 렌더링 후 layout과 paint 전에 동기적으로 실행
실행 순서
- 리액트가 DOM을 업데이트
- useLayoutEffect 실행
- 브라우저에 변경사항을 반영
- useEffect 실행
-
useEffect: mount 직후(painting과 비동기로 실행)
-
useLayoutEffect: redering 직후 mount(painting) 전에 실행
⇒ DOM은 계산 되었지만 화면에 반영되기 전에 하고 싶은 작업이 있을 때 사용
useDebugValue
디버깅 하고 싶은 정보를 해당 훅에 사용하여 확인
-
사용자 정의 훅 내부의 내용에 대한 정보를 남길 수 있음
-
두 번째 인수로 포매팅 함수 전달 → 해당 값이 변경되었을 때만 포매팅 값 노출
⇒ 첫번째 인수의 값이 같으면 포매팅 함수 호출 X
-
오직 다른 훅 내부에서만 실행할 수 있음
훅의 규칙
-
최상위에서만 흑을 호출해야한다.
반복문이나 조건문, 중칩된 함수 내에서 훅을 실행할 수 없다.
이 규칙을 따라야만 컴포넌트가 렌더링될 때마다 항상 동일한 순서로 혹이 호출되는 것을 보장할 수 있다.
-
훅을 호출할 수 있는 것은 리액트 함수형 킴포넌트 혹은 사용자 정의 훅의 두 가지 경우뿐이다.
일반 자바스크립트 할수에 서는 혹을 사용할수 없다.
3.2 사용자 정의 훅과 고차 컴포넌트 중 무엇을 써야 할까?
사용자 정의 훅
서로 다른 컴포넌트 내부에서 같은 로직을 공유하고자 할 때 사용하는 것
- 리액트 훅의 규칙을 따르고
react-hooks/rules-of-hooks
의 도움을 받기 위해서는 use로 시작하는 함수를 만들어야 함
고차 컴포넌트(HOC)
컴포넌트 자체의 로직을 재사용하기 위한 방법
-
고차함수의 일종으로 리액트가 아니더라도 JS 환경에서 사용 가능
-
컴포넌트의 결과물에 영향을 미칠 수 있는 다른 공통된 작업을 처리 가능
-
with로 시작하는 이름을 사용해야 함
⇒ 강제성이 아닌 관습
-
부수효과를 최소화 하기
- HOC는 반드시 컴포넌트를 인수로 받는데, 컴포넌트의 props를 임의로 수정, 추가, 삭제하는 일은 없어야 함
-
반복적으로 컴포넌트를 감쌀 경우 복잡성이 커져, 최소한으로 사용하기
사용자 정의 훅 vs. 고차 컴포넌트
사용자 정의 훅
-
리액트에서 제공하는 훅으로만 공통 로직을 격리할 수 있을 때
-
컴포넌트 내부에 미치는 영향 최소화
→ 훅을 원하는 방향으로만 사용할 수 있음
고차 컴포넌트
- 렌더링의 결과물에도 영향을 미치는 공통 로직일 때
- 고차 컴포넌트가 많아질수록 복잡성이 기하급수적으로 증가하므로 신중하게 사용