리액트 훅은 리액트 클래스형 컴포넌트에서 이용하던 코드를 작성할 필요없이 함수형 컴포넌트에서 다양한 기능을 사용할 수 있게 만들어준 라이브러리, React 16.8버전에 새로 추가된 기능이다. 이는 함수형 컴포넌트에 맞게 만들어진 것으로 함수형 컴포넌트에서만 사용 가능하다.
React에서
use
로 시작하는 함수를 Hook이라고 부른다.
1) Hook을 최상위 레벨에서만 호출한다.
function Counter(){
//함수 컴포넌트의 최상위 레벨에서 사용한다.
const [count, setCount] = useState(0);
//...
}
function useWindowWidth(){
//커스텀 Hook의 최상위 레벨에서 사용한다.
const[width, setWidth] = useState(window.innerWidth);
//...
}
2) 리액트 함수 내에서만 Hook을 호출해야한다.
useState
는 상태를 관리하는 훅으로 컴포넌트에 state변수를 추가할 수 있는 훅이다.
const [state, setState] = useState(initialState)
useEffect
는 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 훅이다.(외부 시스템과 컴포넌트를 동기화)
useEffect(setup, dependencies?)
setup
에는 실행시킬 함수를 넣고 dependencies
는 선택사항으로 의존성 배열을 담는다. componentDidMount
, componentDidupdatea
, componentWillunmount
를 하나의 API로 통합한 것useEffect(() => {
console.log('마운트될 때만 실행됩니다.');
}, []);
useEffect(() => {
console.log(name);
}, [name]);
이 외에도 컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶다면 useEffect
에서 뒷정리(cleanup) 함수를 반환해 주어야한다.
useEffect(() => {
console.log('effect');
console.log(name);
return() => {
console.log('cleanup');
console.log(name);
}
});
렌더링될 때마다 뒷정리 함수가 계속 나타나고, 뒷정리 함수가 호출될 때는 업데이트 되기 직전의 값을 보여준다.
언마운트 될 때만 뒷정리 함수를 호출하고 싶다면 useEffect 함수의 두 번째 파라미터에 비어있는 배열을 넣으면 된다.
useEffect(() => {
console.log('effect');
console.log(name);
return() => {
console.log('cleanup');
console.log(name);
}
},[]);
useReducer
는 컴포넌트에 reducer를 추가하는 훅으로 useState
보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트해 주고 싶을때 사용하는 훅이다.
const [state, dispatch] = useReducer(reducer, initialArg, init?)
reducer
: 현재 상태, 그리고 업데이트를 위해 필요한 정보를 담은 액션(action) 값을 전달받아 새로운 상태를 반환하는 함수initialArg
: 초기 state가 계산되는 값 init
: 선택사항, 초기 state를 반환하는 초기화 함수import { useReducer } from 'react';
function reducer(state, action){
//...
// 불변성을 지키면서 업데이트한 새로운 상태를 반환한다.
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, {age : 42});
//...
// 다른 값들이 필요하다면 추가로 들어감
}
액션값은 주로 다음과 같은 형태로 이루어져있다.
import React, { useReducer } from "react";
function reducer(state, action) {
//action.type에 따라 다른 작업 수행
// eslint-disable-next-line default-case
switch (action.type) {
case "INCREMENT":
return { value: state.value + 1 };
case "DECREMENT":
return { value: state.value - 1 };
default:
return state;
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, { value: 0 });
return (
<div>
<p>
현재 카운터의 값은 <b>{state.value}</b>
입니다.
</p>
<button onClick={() => dispatch({ type: "INCREMENT" })}>+1</button>
<button onClick={() => dispatch({ type: "DECREMENT" })}>-1</button>
</div>
);
}
useMemo
는 재렌더링 사이에 계산 결과를 캐싱할 수 있게 해주는 훅으로 함수형 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있는 훅이다.
const cachedValue = useMemo(calculateValue, dependencies)
calculateValue
: 캐싱하려는 값을 계산하는 함수dependencies
: calculateValue
코드 내에서 참소된 모든 반응형 값들의 목록 import React, { useMemo, useState } from "react";
export default function Average() {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const onChange = (e) => {
setNumber(e.target.value);
};
const onInsert = (e) => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber("");
};
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input type="text" value={number} onChange={onChange} />
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값 : </b> {avg}
</div>
</div>
);
}
const getAverage = (numbers) => {
console.log("평균값 계산 중...");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
getAverage
함수가 호출된다.useCallback
은 리렌더링 간에 함수 정의를 캐싱해주는 훅으로, 만들어 놨던 함수를 재사용할 수 있는 훅이다.
const cachedFn = useCallback(fn, dependencies)
fn
: 캐싱할 함숫값, 생성하고 싶은 함수dependencies
: fn
내에서 참조되는 모든 반응형 값의 목록, 배열(어떤 값이 바뀌었을 때 함수를 새로 생성해야 하는지 명시)import React, { useCallback, useMemo, useState } from "react";
export default function Average() {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []); //컴포넌트가 처음 렌더링될 때만 함수 생성
const onInsert = useCallback(
(e) => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber("");
},
[number, list]
); //number 혹은 list가 바뀌었을때 만 함수 생성
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input type="text" value={number} onChange={onChange} />
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값 : </b> {avg}
</div>
</div>
);
}
const getAverage = (numbers) => {
console.log("평균값 계산 중...");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
useCallback 은 결국 useMemo 에서 함수를 반환하는 상황에서 더 편하게 사용 할 수 있는 훅이다. 숫자, 문자열, 객체 처럼 일반 값을 재사용하기 위해서는 useMemo 를, 그리고 함수를 재사용 하기 위해서는 useCallback 을 사용하면된다.
useRef
는 렌더링에 필요하지 않은 값을 참고할 수 있는 훅으로, 특정 DOM에 접근하여 DOM 조작을 가능하게 하는 훅이다.
const ref = useRef(initialValue)
import React, { useCallback, useMemo, useRef, useState } from "react";
export default function Average() {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const inputEl = useRef(null);
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []); //컴포넌트가 처음 렌더링될 때만 함수 생성
const onInsert = useCallback(
(e) => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber("");
inputEl.current.focus();
},
[number, list]
); //number 혹은 list가 바뀌었을때 만 함수 생성
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input type="text" value={number} onChange={onChange} ref={inputEl} />
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값 : </b> {avg}
</div>
</div>
);
}
const getAverage = (numbers) => {
console.log("평균값 계산 중...");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
useRef
를 사용하여 ref
를 설정하면 useRef
를 통해 만든 객체 안의 current 값이 실제 엘리먼트를 가리킨다. Hook은(함수들은) 값은 재사용이 아니라 로직의 재사용을 위한 것이다!
[ 출처 : https://ko.react.dev/reference/rules/rules-of-hooks ][ 출처 : 리액트를 다루는 기술 - 김민준(velopert) ]