React의 함수형 컴포넌트에서 사용하지 못했던 상태 관리, Lifecycle Method 등 여러 기능을 대체하기 위해 등장한 개념. React 16.8 버전에서 추가되었다.
클래스형 컴포넌트의 단점
- 번들링 시 코드 압축이 잘 되지 않는다.
- 컴파일 단계에서 코드를 최적화하기 어렵게 만든다.
- 서로 연관성이 없는 여러 로직을 하나의 생명 주기 메서드에 작성하는 경우가 많다.
- Hot Reload를 적용한 상황에서 개발자가 개발 시 디버깅하기 힘든 버그를 발생시키는 경우가 있다.
- 재사용 가능한 로직을 만들 때 고차 컴포넌트를 사용하거나 렌더 속성값 패턴을 사용함으로써 React Element Tree가 깊어지면서 성능에 부정적 영향을 끼친다.
Hook의 장점
- 앞서 말한 클래스형 컴포넌트의 단점이 해결한다.
- 비슷한 로직을 한 곳으로 모을 수 있어서 가독성이 좋다.
- 단순한 함수이므로 정적 타입 언어로 타입을 정의하기 쉽다.
- React에서 제공하는 기본 Hook과 개발자가 직접 만든 custom hook을 섞어서 쉽게 custom hook을 만들 수 있다.
Hook의 규칙
- (필수) 무조건 컴포넌트 최상위 레벨에서만 호출되어야 한다.
- (필수) 절대 조건문, 반복문 내에서 사용하면 안 된다.
- (필수) 하나의 컴포넌트에서 Hook을 호출하는 순서는 항상 같아야 한다.
- (필수) 함수형 컴포넌트와 custom hook 안에서만 호출되어야 한다.
- (권고) 네이밍은
use
로 시작되어야 하고, 네이밍에 camel case를 적용한다. (linter가 react hook인지 판별하는 기준)
Hook의 종류
useState
컴포넌트의 상태(state)를 관리하는 hook. 클래스형 컴포넌트의 멤버 변수를 대체하는 효과를 가지고 있다.
// 기본 사용법
const [state, setState] = useState(initialValue);
// useState를 사용함으로 인해 컴포넌트가 리렌더링되더라도 이전 state가 변경되지 않는다.
// 클래스형 컴포넌트의 클래스 프로퍼티(멤버 변수)를 대체하는 효과
// 여러 상태를 한 번에 관리하기
const [stateObject, setStateObject] = useState({
firstdata: 1,
seconddata: 2,
thirddata: 3
});
// **중요** 상태를 변경할 때는 상태의 불변성을 유지해야 한다.
// 객체 불변성 유지하기
const newStateObject = {
...stateObject,
thirddata: 4,
};
setStateObject(newStateObject);
// 배열 불변성 유지하기
const newStateList = stateList.filter(element => element !== targetValue);
setStateList(newStateList);
- useState를 통해 상태를 변경할 때, 해당 상태값이 객체거나 배열일 경우에는 불변성을 꼭 유지해야 한다. 불변성을 지켜주어야만 리액트 컴포넌트에서 상태가 업데이트가 됐음을 감지 할 수 있고 이에 따라 필요한 리렌더링이 진행된다. 상태값을 직접적으로 변경하면 React가 리렌더링을 하지 않는다.
useEffect
클래스형 컴포넌트에서 사용하던 React Lifecycle Method를 대체하는 hook
useEffect(() => {
// ComponentDidMount, ComponentDidUpdate
// 마운트와 업데이트 모두에서 useEffect()가 실행되는 이유: 중간에 prop이나 state가 바뀔 경우를 대비
// cleanUp
return () => {
// ComponentWillUnmount
}
}, deps);
deps
는 의존값이 들어있는 배열로,deps
에 있는 값이 변경되지 않으면 useEffect 내부의 로직이 실행되지 않는다. 이를 통해 컴포넌트가 리렌더링될 때마다 모든 리렌더링에서 useEffect를 실행되지 않도록 할 수 있다.
useReducer
- useState를 대체하여 상태 관리를 할 수 있는 hook
- 복잡한 정적 로직을 만드는 경우, 다음 state가 이전 state에 의존적인 경우 사용하면 좋다.
reducer(state, action)
: 현재 상태와 액션 값을 전달받아 새로운 상태를 반환하는 함수- useReducer를 사용하면 컴포넌트 업데이트 로직(reducer)를 컴포넌트 밖에서 정의할 수 있다는 장점이 생긴다.
- Redux에서 사용하는 action과 다르게 useReducer의 action 객체는 타입에 대한 어떠한 제약도 없다.
const [state, dispatch] = useReducer(reducer, initialStateValue); // state: 현재 가리키고 있는 상태 // dispatch(action): reducer에 action을 전달하여 실행시키는 함수. 파라미터로 액션 값을 넣어주면 리듀서 함수가 호출된다.
useMemo
함수형 컴포넌트 내부에서 발생하는 연산을 최적화하는 hook. 주로 일반적인 값과 객체를 메모이제이션하는 데 쓰인다.
// deps 내부에 있는 값이 바뀔 때만 factory를 연산한다.
useMemo(factory, deps)
deps
는 의존값이 들어있는 배열로,deps
에 있는 값이 변경되지 않으면factory
가 실행되지 않는다는 특징을 이용해서 의존하는 값이 변경될 때만 바뀌는 값을 메모이제이션하는 용도로 사용한다.
useCallback
함수형 컴포넌트의 렌더링 성능을 최적화하는 hook. 주로 함수를 메모이제이션하는 데 쓰인다.
- 사실상 useMemo와 똑같이 사용할 수 있다. (일반 값을 재사용할 때 useMemo, 함수를 재사용할 때 useCallback을 쓰면 된다.)
useCallback(callback, deps)
과useMemo(() => callback, deps)
은 정확히 똑같이 작동한다.// deps 내부에 있는 값이 바뀔 때만 callback 함수를 생성한다. // (단, !deps.length일 경우 컴포넌트가 처음 렌더링될 때만 함수를 생성한다.) useCallback(callback, deps)
useRef
- JavaScript의
querySelector()
,getElementById()
메소드를 대체하는 hook - DOM을 직접 선택해야 하는 상황(포커스, 스크롤, element 속성 가져오기 등)에서 사용한다.
- useRef는 호출되어도 리렌더링이 발생하지 않기 때문에 로컬 변수를 사용할 때 활용하기도 한다.
// 컴포넌트 로컬 변수로 useRef 사용하기 const SampleComp = () => { const id = useRef(1); // id가 SampleComp의 로컬 변수가 된다. }
Custom Hook
- 여러 컴포넌트에서 자주 비슷한 hooks를 사용하게 될 경우, Custom hook을 만드는 것이 좋다.
- Custom hook도 결국 hook이기 때문에, 위에서 언급한 hook의 규칙을 지켜야 한다.
// 커스텀 hook 만들기
import { useReducer } from 'react';
function reducer(state, action) {
return {
...state,
[action.name]: action.value;
}
}
export default function useInputs(initialForm) {
const [state, dispatch] = useReducer(reducer, initialForm);
const onChange = e => {
dispatch(e.target);
};
return [state, onChange];
}
// 커스텀 hook 사용하기
import useInputs from './useInputs';
const Info = () => {
const [state, onChange] = useInputs({
id: '',
name: '',
password: '',
});
const { id, name, password } = state;
// ...
}
'Web > React' 카테고리의 다른 글
프론트엔드 웹 서비스에서 우아하게 비동기 처리하기 (0) | 2022.01.02 |
---|---|
CRA 안 쓰고 Webpack으로 구축한 React 프로젝트 환경변수 설정하기 ^^^^^^ (0) | 2021.12.21 |