3. 훅: 함수 컴포넌트에 state와 다른 기능 추가하기

히니·1일 전

react-interview-guide

목록 보기
3/3

훅 소개와 목적

  1. 훅이란?
    :: 함수 컴포넌트가 리액트 생명주기와 state를 연결하는 수단이다.

  2. 훅의 도입 배경은?
    :: 컴포트간 재사용이 어렵고, 로직의 캡슐화가 어려웠다.

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>
  );
}

훅을 이용한 지역 state 관리

  1. useState란?
    :: 함수 컴포넌트에 state를 추가하고 관리할 수 있는 훅이다.

  2. updater함수를 사용하는것이 항상 좋은가?
    :: 동일한 이벤트 핸들러 내에서는 여러 state 업데이트를 하는 경우 예상하는 데이터 결과를 받기 위해서는 updater함수를 사용하는 것이 좋다.


const handleClick = () => {
	setCounter((prevData) => prevData + 1);
  	setCounter((prevData) => prevData + 1);
  	setCounter((prevData) => prevData + 1);

}

만약에 setCounter(data + 1) 3번 호출하면 기존 값에서 +1된 값만 나올 수 있다. 그렇기 떄문에, 하나의 이벤트 핸들러에 state를 여러번 호출해야한다면 예상된 결과값을 얻기 위해서는 updater함수를 호출하는것이 좋다.

  1. useReducer훅이란 무엇인가? 어떻게 사용하는가?

:: 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인자와 이전 상태값을 전달받아 값을 조작한다.

훅을 이용한 전역 state를 관리

리액트 애플리케이션에서 부수 효과 실행하기

1.useEffect훅 내의 반응형 의존성이 로직에 어떤 영향을 미치는가?

아래와 같은 특성이 있다.


useEffect(() => {
},[]) // mounted시에 실행

useEffect(() => {
},[state]) // mounted와 state값이 변경이 될떄 실행

useEffect(() => {
}) // 리렌더링 시에
  1. useEffect 훅 내에서 설정 및 정리 함수는 얼마나 자주 호출되는가?
    :: mounted, 리렌더링, unmounted직전에 호출된다.
useEffect(() => {
  // 1. 마운트된 직후 (Mounted)
  console.log("마운트됨!");

  return () => {
    // 2. 언마운트 직전 (Before Unmount)
    console.log("사라지기 직전!");
  };
}, []);
  1. useLayoutEffect 훅은 무엇이고 어떻게 동작하는가?
    :: 화면에 다시 그리기전에 호출되는 특수한 종류의 effect훅으로, state가 업데이트할때 컴포넌트가 깜빡이는 경우에 사용된다.

애플리케이션 최적화

  1. 메모이제이션이 무엇인가? 리액트에서 어떻게 구현할 수 있는가?
    :: 값비싼 함수의 결과를 캐싱 해서 웹 애플리 케이션에서 속도를 높일 수 있는 훅이다. 리액트에서 useMemo()와 useCallback()훅을 통해 구현 할 수 있다.

  2. useMemo() 훅을 설명 할 수 있는가?
    :: useMemo는 함수의 계산 결과를 캐싱하는데 사용된다.

5.1 useMemo를 사용하면 좋을떄는?
:: 1. 렌더링시에 정렬, 필터링과 같은 비용이 많이 드는 계산 2. prop변경이 없을때 리렌더링을 건너뛰려는 경우

(프로파일러 섹션은 지연되는 컴포넌트를 식별할 수 있는 탭이다)

  1. 언제 useMemo 훅 대신 useCallback훅을 사용해야 하는가?
    :: props로 함수를 전달할경우 매번 새로운 함수가 생성이 된다. 그렇기 때문에 useCallback을 사용해서 함수가 props로 전달시에 useCallback으로 사용시 리렌더링을 막을 수 있다.

ref훅을 사용한 DOM 노드 접근

ref는 내장브라우저 API와 같이 외부 시스템을 이용할때가 유용하다.

  1. ref 콘텐츠가 재생성되는것을 어떻게 막는가?
    :: useRef 훅은 초깃값을 useState 훅처럼 인자를 받는다. 만약에 useRef의 초깃값을 무거운 객체로 한다면 리렌더링시마다 계속 새로운 값으로 계산된다. 이는 애플리케이션 성능에 영향을 미칠 수 있다.
function MyComponent() {
  const ref = useRef(null); // 처음엔 가벼운 null로 시작

  // 실제 값이 필요할 때만 체크해서 생성
  function getHeavyObject() {
    if (ref.current === null) {
      ref.current = new HeavyObject(); // 딱 한 번만 실행됨
    }
    return ref.current;
  }

  // ... 필요할 때 getHeavyObject() 호출
}
  1. 렌더링 메서드에서 ref에 접근하는것이 가능한가?
    가능하지만 렌더링 프로세스에서 ref.current값을 읽거나 쓰는것을 권장하지 않는다.
  1. ref 인스턴스에서 일부 메서드을 노출 시키는 방법은 무엇인가?
useImperativeHandle(ref,() => {
	open : () => ref.current.invokeDialog(),
    close :  () => ref.current.closeDialog(),
	reset : () => ref.current.clearData()
})
profile
안녕하세요

0개의 댓글