class의 단점을 보완하면서 라이프 사이클 등과 관련된 함수를 재사용 가능하게 한다.
Hook을 이용하여 기존의 class 바탕의 코드를 작성할 필요없이 상태 값과 여러 react 기능을 사용할 수 있다.
컴포넌트 사이에서 상태 로직 재사용의 어려움
Hook은 계층의 변화 없이 상태 관련 로직을 재사용할 수 있도록 도와준다.
복잡한 컴포넌트 이해의 어려움
복잡한 상태 관련 로직들과 사이드 이펙트가 있는 컴포넌트들을 사용할 때 관련 없는 로직이 섞이곤 한다. DidMount, DidUpdate, WillUnMount 등등 ... 특히 얘네는 한 공간 안에 묶여있어서 작게 분리하는 것이 불가능하다.
Hook을 이용해 서로 비슷한 것을 하는 작은 함수의 묶음으로 컴포넌트를 나눌 수 있다.
Hook은 자바스크립트 함수이지만, 두 가지 규칙을 준수해야한다.
최상위에서만 Hook을 호출해야한다.
반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하지 않도록 한다.
React 함수 컴포넌트 내에서만 Hook을 호출해야한다. (+ custom Hook)
일반 JS 함수에서는 Hook을 호출하지 않도록 한다.
함수 컴포넌트에서는 React state를 사용할 수 없는데, useState 함수를 사용하면 함수 컴포넌트 안에서 state를 사용할 수 있다.
// class component
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
// functional component with hook
import React, { useState } from 'react';
function Example() {
// 새로운 state 변수를 선언하고, 이것을 count라 부르겠습니다.
const [count, setCount] = useState(0);
useState가 하는 일
state 변수를 선언할 수 있다.
클래스 컴포넌트의 this.state
가 제공하는 기능과 같다.
useState의 인자
state의 초기값을 넘겨준다. 클래스와 달리 객체일 필요는 없고, 숫자/문자 타입을 가질 수 있다.
useState의 반환값
state 변수, 해당 변수를 갱신할 수 있는 함수
이 두 가지 쌍을 반환한다.
effect hook을 사용하면 함수 컴포넌트에서 side effect를 수행할 수 있다.
데이터 가져오기, 구독(subscription) 설정하기, 수동으로 React 컴포넌트의 DOM을 수정하는 것까지 이 모든 것이 side effects 이다.
클래스 컴포넌트에서의 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것과 같다.
React가 DOM을 업데이트한 뒤 추가로 코드를 실행해야 하는 경우가 있다. 네트워크 리퀘스트, DOM 수동 조작, 로깅 등은 정리(clean-up)가 필요 없는 경우들이다. 이러한 예들은 실행 이후 신경 쓸 것이 없다.
useEffect가 하는 일
react에게 컴포넌트가 렌더링 이후에 어떤 일을 수행해야하는지 말한다.
우리가 넘긴 함수를 기억했다가 DOM이 업데이트를 수행한 이후에 불러낸다.
useEffect를 컴포넌트 내에서 부르는 이유
컴포넌트 내부에 두면 effect를 통해 state 변수에 접근할 수 있게 된다.
외부 데이터에 구독을 설정해야하는 경우를 생각하자. 이런 경우에서 메모리 누수가 발생하지 않도록 정리를 꼭 해주어야 한다.
클래스 컴포넌트에서는 구독과 구독해지를 한 공간에서 하지 못한다.
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// effect 이후에 어떻게 정리(clean-up)할 것인지 표시합니다.
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
effect에서 함수를 반환하는 이유
effect를 위한 추가적인 정리 메커니즘이다.
react가 effect를 정리하는 시점
컴포넌트가 마운트 해제되는 때에 정리를 실행한다.
정리 함수는 반드시 유명함수를 반환해야하는 것은 아니다. 화살표 함수를 쓰거나 다른 이름으로 불러도 된다.
useEffect의 선택적 인수인 두번째 인수로 배열을 넘기면 특정 값에 대한 랜더링을 제어할 수 있다.
아무 값을 주지 않을 경우
항상 렌더링 된다.
빈 배열을 줄 경우
맨 처음에 한 번만 렌더링 된다.
어떤 값이 들어있는 배열
그 값이 변경 될 때만 렌더링 된다.
자신만의 Hook을 만들어 컴포넌트 로직을 함수로 뽑아내어 재사용할 수 있다.
custom hook을 만들 때는 이름을 use 로 시작하게 한다. 관습이지만 아주 중요하다.
예시로 채팅 애플리케이션에서 친구가 온라인 상태인지에 대한 메시지를 표시하는 컴포넌트를 보자.
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
// 주목
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
// 주목
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
다음으로는 온라인 상태의 사용자들의 이름을 초록색으로 표시하는 상황을 보자.
import React, { useState, useEffect } from 'react';
function FriendListItem(props) {
// 주목
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
// 주목
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
주석으로 감싸놓은 부분이 중복되고 있다.
중복되는 부분을 사용자 custom hook으로 만들어 재사용할 수 있다.
import { useState, useEffect } from 'react';
// 중복되는 부분의 custom hook
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
같은 Hook을 사용하는 두 컴포넌트 내의 state와 effect는 완전히 독립적이다.
각각의 Hook에 대한 호출은 서로 독립된 state를 받는다.
useContext
useReducer
useState의 대체 함수.
(state, action) => newState의 형태로 reducer를 받고 dispatch 메서드와 짝의 형태로 현재 state를 반환한다.
useCallback
useMemo
useRef
useImperativeHandle
useLayoutEffect
useDebugValue