[React] React Hooks 총 정리 -2

안치영·2023년 1월 19일
0

React

목록 보기
15/15

이 포스트는 전에 올렸던 React Hooks의 종류 및 개념에 대한 총 정리를 이어서 작성하는 글 입니다.

useReducer()

  • useState를 대체할 수 있는 함수이다.
  • state보다 복잡한 상태관리가 필요한 경우 reducer를 사용할 수 있다.
  • reducer는 이전 상태와 Action을 합쳐, 새로운 state를 만드는 조작을 말한다.
const [state, dispatch] = useReducer(reducer, initialState, init);
  • state : 컴포넌트에서 사용할 상태
  • dispatch : reducer함수 실행, 컴포넌트 내 state업데이트를 위한 함수
  • reducer : 컴포넌트 외부에서 state를 업데이트, 현재state, action객체를 인자로 받아, 기존의 state를 대체하여 새로운 state를 반환하는 함수
  • initialState : 초기 state
  • init : 초기 함수 (초기 state를 조금 지연해서 생성하기 위해 사용)

카운터로 useReducer 사용 예시

import React, { useReducer } from "react";

function init(initialState) {
  return { count: initialState }; 
}

function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + action.payload };
    case "DECREMENT":
      return { count: state.count - action.payload };
    case "RESET":
      return init(action.payload);
    default:
      throw new Error("unsupported action type: ", action.type);
  }
}

const Counter = ({ initialCount }) => {
  const [state, dispatch] = useReducer(reducer, initialCount, init);

  return (
    <>
      <h2>{state.count}</h2>
      <button onClick={() => dispatch({ type: "RESET", payload: 0 })}>
        초기화
      </button>
      <button onClick={() => dispatch({ type: "INCREMENT", payload: 1 })}>
        증가
      </button>
      <button onClick={() => dispatch({ type: "DECREMENT", payload: 1 })}>
        감소
      </button>
      <button onClick={() => dispatch({ type: "errrrrrrrrr", payload: 1 })}>
        에러
      </button>
    </>
  );
};

export default Counter;

useCallback()

  • 특정 함수를 새로 만들지 않고 재사용하고 싶을 때 사용한다.
  • 리액트의 렌더링 성능을 위해 제공되는 훅이다.
  • 훅을 사용하면서 컴포넌트가 렌더링 될때마다, 함수를 생성해 자식 컴포넌트의 속성으로 넘겨준다.
  • 두번째 파라미터는 의존성 배열을 넣어주면 된다. (렌더링 조건 부여)
function App() {
  const [name, setName] = useState('');
  /*
  const onSave = () => {};
  useCallback을 사용하지 않으면, Profile 컴포넌트에서 React.memo를 사용해도 이전 onSave와 이후 onSave가 
  매번 다르게 되기 때문에 매번 렌더링이 된다. 그러므로, Profile컴포넌트의 리렌더링을 방지하기 위해 useCallback을 
  사용한다.
  */
  const onSave = useCallback(() => {
    console.log(name)
  },[name]);

  return (
    <div className="App">
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <Profile onSave={onSave} />
    </div>
  );
}

useMemo()

  • 컴포넌트의 성능을 최적화 시킬 수 있는 대표적인 React Hooks이다.
  • Memo는 Memoization을 뜻한다.
  • 컴포넌트가 반복적으로 렌더링 되어도, 함수를 다시 호출하지 않고 이전에 이미 계산된 결과 값을 메모리에서 꺼내와서 재사용할 수 있게 해준다.
  • 두번째 인자로 의존성배열을 넣어주면 된다. (렌더링 조건 부여)
  • 메모리를 소비해서 값을 저장해놓는 것이기 때문에 필요한 곳에만 사용한다.

예시

object는 객체 타입이기 때문에, 원시타입과는 다르게 값이 저장될 때 주소 값으로 저장이된다. 즉, 메모리 상의 주소가 다르게 저장되어 있는 것인데, 예시의 객체는 눈으로 보이기에는 똑같지만 저장된 메모리 상의 주소가 완전히 다르기 때문에 useEffect의 lunch는 변경이 되었다고 생각할 수 있다.

const lunch = { meal: isKorean ? '한식' : '일식' };

useEffect(() => {
    console.log('useEffect... 호출');
}, [lunch])

따라서, 이것을 해결해 주려면 useMemo를 사용해서 memoization해주면 된다.

import { useMemo, useEffect, useState } from 'react'; 

function App() {
  const [number, setNumber] = useState(0);
  const [isKorean, setIsKorean] = useState(true);
  
  const lunch = useMemo(() => {
    return {
      meal: isKorean ? '한식' : '일식'
    }
  }, [isKorean])

  useEffect(() => {
    console.log('useEffect... 호출');
  }, [lunch])

  return (
    <div>
        <h2>하루에 몇 끼 먹어요?</h2>
        <input type="number" value={number} onChange={(e) => setNumber(e.target.value)}/>
        <hr/>

        <h2>어느 음식 먹어요?</h2>
        <p>종류: {lunch.meal}</p>
        <button onClick={() => setIsKorean(!isKorean)}>Update</button>
    </div>
  );
}

export default App;

0개의 댓글