훅이란?
:: 함수 컴포넌트가 리액트 생명주기와 state를 연결하는 수단이다.
훅의 도입 배경은?
:: 컴포트간 재사용이 어렵고, 로직의 캡슐화가 어려웠다.
e.g) 창의 너비를 감지하는 custom hook을 생성해보자
import { useState, useEffect } from 'react';
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
// 언마운트 시 리스너 제거 (정리)
return () => window.removeEventListener('resize', handleResize);
}, []);
return width; // 현재 너비 값만 반환
}
위에 custom hook 을 A컴포넌트에서 사용할 수 있고 B컴포넌트에서 사용할 수 있다.
function Navbar() {
const width = useWindowWidth(); // 로직 재사용
return (
<nav>
{width < 768 ? <MobileMenu /> : <DesktopMenu />}
</nav>
);
}
useState란?
:: 함수 컴포넌트에 state를 추가하고 관리할 수 있는 훅이다.
updater함수를 사용하는것이 항상 좋은가?
:: 동일한 이벤트 핸들러 내에서는 여러 state 업데이트를 하는 경우 예상하는 데이터 결과를 받기 위해서는 updater함수를 사용하는 것이 좋다.
const handleClick = () => {
setCounter((prevData) => prevData + 1);
setCounter((prevData) => prevData + 1);
setCounter((prevData) => prevData + 1);
}
만약에 setCounter(data + 1) 3번 호출하면 기존 값에서 +1된 값만 나올 수 있다. 그렇기 떄문에, 하나의 이벤트 핸들러에 state를 여러번 호출해야한다면 예상된 결과값을 얻기 위해서는 updater함수를 호출하는것이 좋다.
:: useReducer는 useState의 대체제이다. 값이 여러 복잡한경우 state로 관리하지 않고 useReducer를 통해 값을 관리한다.
e.g)
CounterReducer.tsx를 작성해준다.
interface CounterState {
count: number;
}
interface CounterAction {
type: 'INCREMENT' | 'DECREMENT';
}
function counterReducer(state: CounterState, action: CounterAction): CounterState {
switch (action.type) {
case 'INCREMENT':
return {count: state.count + 1};
case 'DECREMENT':
return {count: state.count - 1};
default:
throw new Error('Unhandled action type');
}
}
const initialState: CounterState = {count: 0};
export {
counterReducer,
initialState
}
Counter.tsx에서 사용예시
import {useReducer} from 'react';
import {counterReducer, initialState} from "../reducer/count";
function Calculator() {
const [state, dispatch] = useReducer(counterReducer, initialState);
return (
<>
<div>
<p>Count : {state.count}</p>
<button onClick={() => dispatch({type: 'INCREMENT'})}>+</button>
<button onClick={() => dispatch({type: 'DECREMENT'})}>-</button>
</div>
</>
)
}
export default Calculator;
실행과정을 설명하자면, 사용자가 이벤트를 발생하면 등록된 이벤트 핸들러 내에서 상태를 바꾸고 싶은 dispatch를 호출한다. 이때 호출된 dispatch 안에 action인자를 넣어 호출한다.
그러면 등록된 reducer는 action인자와 이전 상태값을 전달받아 값을 조작한다.
1.useEffect훅 내의 반응형 의존성이 로직에 어떤 영향을 미치는가?
아래와 같은 특성이 있다.
useEffect(() => {
},[]) // mounted시에 실행
useEffect(() => {
},[state]) // mounted와 state값이 변경이 될떄 실행
useEffect(() => {
}) // 리렌더링 시에
useEffect(() => {
// 1. 마운트된 직후 (Mounted)
console.log("마운트됨!");
return () => {
// 2. 언마운트 직전 (Before Unmount)
console.log("사라지기 직전!");
};
}, []);
메모이제이션이 무엇인가? 리액트에서 어떻게 구현할 수 있는가?
:: 값비싼 함수의 결과를 캐싱 해서 웹 애플리 케이션에서 속도를 높일 수 있는 훅이다. 리액트에서 useMemo()와 useCallback()훅을 통해 구현 할 수 있다.
useMemo() 훅을 설명 할 수 있는가?
:: useMemo는 함수의 계산 결과를 캐싱하는데 사용된다.
5.1 useMemo를 사용하면 좋을떄는?
:: 1. 렌더링시에 정렬, 필터링과 같은 비용이 많이 드는 계산 2. prop변경이 없을때 리렌더링을 건너뛰려는 경우
(프로파일러 섹션은 지연되는 컴포넌트를 식별할 수 있는 탭이다)
ref는 내장브라우저 API와 같이 외부 시스템을 이용할때가 유용하다.
function MyComponent() {
const ref = useRef(null); // 처음엔 가벼운 null로 시작
// 실제 값이 필요할 때만 체크해서 생성
function getHeavyObject() {
if (ref.current === null) {
ref.current = new HeavyObject(); // 딱 한 번만 실행됨
}
return ref.current;
}
// ... 필요할 때 getHeavyObject() 호출
}
useImperativeHandle(ref,() => {
open : () => ref.current.invokeDialog(),
close : () => ref.current.closeDialog(),
reset : () => ref.current.clearData()
})