React Hooks는 ReactConf 2018에서 발표된 class 없이 state를 사용할 수 있는 새로운 기능이다.
Hooks를 사용하면 클래스 컴포넌트를 작성하지 않고도 상태 관리와 기타 React 기능을 사용할 수 있다.
React Hooks는 클래스 컴포넌트로 사용되면서 느꼈던 불편함이나 문제들을 해결하기 위해 등장했다.
원래 React는 주로 클래스 컴포넌트를 사용하고 React Hooks는 함수형 컴포넌트를 사용한다. React Hooks를 사용하면 함수형 컴포넌트에서도 강력한 상태 관리와 생명주기 메서드를 활용할 수 있어 현대적인 리액트 개발에서 많이 사용되고 있다.
- 클래스 컴포넌트: 초기의 리액트 컴포넌트 방식. 더 많은 기능을 제공하지만 성능이 느림
- 함수형 컴포넌트: 2018년 이후 등장. React Hooks 사용 가능. 더 적은 기능을 제공하지만 더 빠른 성능 제공
그렇다면 함수형 컴포넌트에서 더 적은 기능을 제공하는데, 어떤 기능을 쓸 수 없는 것 일까?

출처 : https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
함수형에서 못 쓰는 것은 리액트 생명주기다. 리액트를 실행하고 업데이트하고 멈추는 중요한 생명주기를 함수형에서는 못 사용했기 때문에 함수형이 더 빠르고 간결하더라도 클랙스형을 써왔지만, 2018년 이후 Hooks가 등장하면서 생명주기 부분을 대체할 수 있게 되었다.
상태 변수를 선언하고, 상태를 갱신할 수 있는 함수와 함께 반환한다. 초기 상태를 인수로 받아들인다.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
기본적으로 컴포넌트가 마운트, 업데이트, 언마운트 될 때 호출된다. 인수로 배열을 받아 특정 값이 변경될 때만 실행되도록 할 수 있다.
import React, { useEffect, useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // count가 변경될 때마다 effect가 실행됨
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
컨텍스트(Context)를 사용하여 트리 내에서 데이터를 전역적으로 공유할 수 있다.
import React, { useContext } from 'react';
const MyContext = React.createContext();
function MyComponent() {
const value = useContext(MyContext);
return <div>{value}</div>;
}
function App() {
return (
<MyContext.Provider value="Hello, World!">
<MyComponent />
</MyContext.Provider>
);
}
상태 로직을 컴포넌트 외부로 이동시켜 복잡한 상태 관리를 수행할 때 유용하다. Redux와 유사한 패턴을 따른다.
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
성능 최적화를 위해 메모이제이션된 값을 반환. 특정 값이 변경될 때만 계산을 수행하도록 할 수 있다.
import React, { useMemo, useState } from 'react';
function ExpensiveComponent({ input }) {
const computeExpensiveValue = (input) => {
// ... 비용이 큰 계산 ...
return input * 2;
};
const expensiveValue = useMemo(() => computeExpensiveValue(input), [input]);
return <div>{expensiveValue}</div>;
}
메모이제이션된 콜백 함수를 반환. 컴포넌트가 리렌더링될 때 동일한 함수 인스턴스를 유지하도록 한다.
import React, { useCallback, useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((c) => c + 1);
}, []); // 빈 배열을 전달하여 함수가 최초 렌더링 시에만 생성되도록 함
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}