리액트 훅이란 리액트 16.8 버전에 정식으로 추가된 것으로 함수 컴포넌트가 리액트의 state, ref 등의 다양한 기능을 활용할 수 있도록 해주는 것이다.
useState
함수 컴포넌트 내부에서 상태를 정의하고 관리할 수 있게 해주는 훅이다.
import { useState } from "react";
const [state, setState] = useState(initialState);
state가 상태이고, setState를 통해 해당 state의 값을 변경할 수 있다.
알고 넘어갈 점
useState의 반환값이 객체가 아닌 배열인 이유는 비구조화 할당을 할 때 쉽게 state와 setState의 이름을 정할 수 있게 하기 위해서다.
useState가 아닌 일반 변수를 사용하면 나타나는 일
function Component() {
const [, triggerRender] = useState();
let state = "hello";
function handleButtonClick() {
state = "hi";
triggerRender();
}
return (
<>
<h1>{state}</h1>
<button onClick={handleButtonClick}>hi</button>
</>
);
}
함수 컴포넌트의 렌더링은 함수를 재실행하는 것으로,
변수의 값을 변경시켜도 다시 컴포넌트 함수가 실행되면서 변수는 재선언되고 초기화되므로 'hi'
로 렌더링 되지 않는다.
useState의 실제 구현
useState는 이를 피하기 위해 자바스크립트의 클로저(Closure)를 활용하여 컴포넌트 외부의 리액트 컨텍스트에 저장되어 렌더링 시점의 값을 정확히 불러올 수 있게 되었다.
useState의 lazy initialization
useState의 인자로 특정한 값을 반환하는 함수를 넣어줄 수 있는데,
state가 처음 만들어 질 시(최초 컴포넌트 마운트 시) useState의 초깃값을 함수를 실행하여 반환해준다.
lazy initialization을 하지 않고 직접 값을 넣어주면 리렌더링시마다 해당 로직을 실행한다.
그 과정에 실행 비용이 많이 드는 로직이 있다면 lazy initialization을 하는 것이 좋다.
const [state, setState] = useState(expensiveBehavior()); // 리렌더링 시마다 실행
const [state, setState] = useState(() => {
return expensiveBehavior();
}); // 리렌더링 시 실행되지 않음
useEffect
useEffect는 컴포넌트의 여러 값들을 활용해 동기적으로 부수 효과를 만드는 메커니즘이다.
컴포넌트가 브라우저에 렌더링 된 후 실행된다. (커밋 단계 이후)
function Component() {
useEffect(
function effectFunction() {
// do something
},
[props, state]
);
}
useEffect의 인자는 총 두 가지이다.
실행될 부수 효과 함수
- 클린업 함수 반환 (Optional)
의존성 배열 (Optional)
effect function
실제로 실행되는 로직이다.
clean up function
부수 효과 함수(effect function)가 반환할 수 있는 함수로 리렌더링 시 이전의 부수효과를 청소해주는 함수다.
dependency array
의존성 배열 내의 값이 변경되면 부수 효과 함수가 실행된다.
빈 배열을 넣으면 최초 마운트 시에만 실행되고, 생략하면 모든 렌더링마다 실행된다.
궁금한 점
의존성 배열을 비교할 때 배열 자체에 shallowEqual을 할까? 아니면 배열 내부의 값마다 shallowEqual을 할까?
useEffect 주의점
react-hooks/exhaustive-deps
경고를 최대한 무시하지 말아야 한다.
이는 생명주기의 componentDidMount에 기반한 접근법으로, 가급적이면 사용하면 안된다.
부수 효과 함수에서 의존성 배열에 들어있지 않은 값에 접근한다는 것은 state, props의 변경과 useEffect의 부수 효과가 별개로 작동하게 된다는 뜻이 된다.
그런 상황은 useEffect 내 부수효과가 실행될 위치가 잘못됐을 가능성이 크다. 그런 로직이 정말 필요한지 다시 검토해보아야 한다.
- 부수 효과 함수을 기명 함수로 사용해야 한다.
useEffect의 부수 효과 함수를 위의 예제와 같이 같이 기명 함수로 사용하면 함수의 역할을 정해주게 되고 로직 파악이 용이해진다.
- useEffect의 크기를 작게 유지해야 한다.
useEffect는 부수 효과를 담는 훅이기 때문에 부수 효과가 커지게 되면 실행 성능과 유지보수 측면에서 안좋은 영향이 있다.
- 불필요한 외부 함수를 만들면 안된다.
부수 효과 함수 외부에서 함수를 만들고 내부에서 그걸 사용하게 되면 복잡한 로직이 된다. 내부에서 정의해서 사용하는 편이 낫다.