리액트 컴포넌트는 2종류로 클래스 컴포넌트와 함수형 컴포넌트가 있으며 이번 포스팅에서는 함수형 컴포넌트에 대해서만 다루겠습니다.
const Example = (props) => {
// 여기서 Hook을 사용할 수 있다.
return <div />;
}
function Example(props) {
// 여기서 Hook을 사용할 수 있다.
return <div />;
}
Hook은 React 버전 16.8부터 새로 추가된 기능으로
클래스 컴포넌트와 생명주기 메서드를 이용하여 작업을 하던 기존 방식에서 벗어나 함수형 컴포넌트에서도 더 직관적인 함수를 이용하여 작업할 수 있게 만든 것이다.
Hook은 javascript함수이지만 일반적인 js함수에서는 호출하면 안된다.
위의 두 가지 규칙을 지킬경우 컴포넌트가 렌더링될때마다 항상 동일한 순서로 Hook이 호출되는것을 보장하며,
useState와 useEffect가 여러번 호출되는 중에도 Hook의 상태를 올바르게 유지할 수 있도록 해준다.
const [state, setState] = useState(initialState);
상태를 설정할 때 사용하는 훅 API로 클래스 컴포넌트의 생명주기 메소드 constructor()에서 상태를 초기화하는 것과 비슷한 역할을 하는 API입니다. 이 API는 인자로 초기 상태(initialState)를 받고, 반환 값으로 현재 상태(state)와 상태를 설정하는 함수(setState)를 반환합니다.
이때, 초기 상태와 현재 상태, 상태를 설정하는 함수는 항상 위와 같은 이름을 가질 필요가 없습니다. 다만, 상태를 설정하는 함수의 이름은 보통 set상태로 짓습니다.
초기 상태는 숫자나 문자열, 배열, 객체 등을 이용하여 설정할 수 있는데요, 이때 콜백 함수를 이용한다면 초기 렌더링 시에 콜백 함수가 반환하는 값을 초기 상태로 갖게 됩니다. 이렇게 설정된 상태는 추후에 상태 설정 함수를 이용하여 변경할 수 있으며, 이 훅을 통해 설정된 상태는 컴포넌트가 다시 렌더링 되어도 유지된다는 장점이 있습니다.
또한, 하나의 컴포넌트에 여러개의 상태가 선언될 수 있습니다.
하지만 하나의 상태를 여러 컴포넌트에서 사용하게 되는 경우, 매번 props를 전달해야 하여 불필요한 코드 중복이 발생할 수 있으며 오류가 생길 수 있다는 단점이 있습니다. 이 경우에는 Redux 또는 Recoil과 같은 전역 상태 라이브러리를 이용하여 관리하는 것이 권장됩니다.
useEffect(callback, dependency);
클래스 컴포넌트의 생명주기 메소드 componentDidMount(), componentDidUpdate, componentWillUnmount()를 통합한 것과 같은 API로 side effect를 발생하는 작업을 수행하는 훅 API입니다. Side effect란 다른 컴포넌트에 영향을 줄 수 있고 렌더링 과정에서는 구현할 수 없는 작업을 일컫는데요, 그 예로는 컴포넌트 안에서 데이터 가져오거나 구독하기, DOM을 직접 조작하기 등이 있습니다.
useEffect(() => {
// side effect를 발생하는 작업
const timerId = setTimeout( () => console.log('useEffect') );
// side effect를 발생하는 작업을 정리
return () => clearTimeout(timerId);
});
앞서 언급한 클래스 컴포넌트의 생명주기 메서드 componentDidMount()와 componentDidUpdate()는 컴포넌트가 렌더링 될 때마다 side effect가 발생하는 작업을 수행하고, componentDidUmount()는 컴포넌트가 언마운트될 때 이 작업에 대한 정리 작업을 수행합니다.
이러한 세 메소드를 합친 것과 같은 useEffect()는 첫 번째 인자로 전달받은 콜백 함수 내부에서 side effect가 발생하는 작업을 수행하고, 이에 대한 정리 작업을 수행하는 cleanup 함수를 반환합니다.
또한, 위와 같이 두번째 인자로 아무것도 넣지 않은 경우에는 매 렌더링 시마다 콜백 함수를 실행하고, 다음 렌더링이 실행되기 전에는 cleanup 함수를 실행합니다.
useEffect(() => {
console.log('useEffect');
}, []);
두 번째 인자에 빈 배열을 넣는 경우, 마운트 될 때에 콜백 함수 내부를 실행하고 언마운트 될 때에 cleanup 함수를 실행합니다. 이를 사용할 때에는 마운트 될 때의 state값과 props값이 언마운트될 때까지 유지된다는 점을 주의해야 합니다.
useEffect(() => {
console.log('useEffect');
}, [state]);
특정 값이 업데이트 되었을 때만 실행하고 싶은 경우에는 두 번째 인자에 특정 값을 담은 배열을 넣어주면 됩니다. 또한, 배열에는 여러 개의 값을 넣을 수 있고 일반적으로는 콜백 함수 내에 사용된 지역 변수를 배열에 담는 편이지만 편의에 따라 해당 값을 안 넣을 수도 있고, 콜백 함수 내부에서 사용하지 않는 값을 넣을 수 있습니다. 이때, 리액트 측에서 setState 함수는 동일성을 보장하고 변경되지 않는다고 하므로 넣어줄 필요가 없습니다.