프론트엔드(Web)/React

리액트 메모이제이션- useCallback,useMemo,React.memo,useReducer

만능 엔터테이너 2024. 8. 27. 17:35
728x90
반응형
SMALL

useCallback

useCallback 훅은 함수형 컴포넌트에서 함수를 메모이제이션 하는 데 사용됩니다. 여기서 메모이제이션이란, 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장하여 사용함으로써 동일한 계산의 반복 수행을 제거하여 성능 향상을 도모하는 기술을 말합니다.

문법

const cachedFn = useCallback(fn, dependencies)

예제

ex1)

import React, { useState } from "react";

const functionSet = new Set();
const App = () => {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(0);
  const decrement = () => {
    setCount(count - 1);
  };
  const increment = () => {
    setCount(count + 1);
  };
  const otherIncrement = () => {
    setCount2(count2 + 1);
  };

  functionSet.add(decrement);
  functionSet.add(increment);
  functionSet.add(otherIncrement);

  console.log(functionSet);
  return (
    <div>
      <h1>Count:{count}</h1>
      <button onClick={decrement}>-</button>
      <button onClick={increment}>+</button>
      <hr />
      <h1>Count2:{count2}</h1>
      <button onClick={otherIncrement}>+</button>
    </div>
  );
};

export default App;

app.tsx

import { useCallback, useState } from "react";
import SubInput from "./components/SubInput";

const userInitailized = ["jack", "john", "jane", "Smith"];
const App = () => {
  const [user, setUser] = useState(userInitailized);

  const onChangeHandler = useCallback((text: string) => {
    const filterUser = user.filter((item) => item.includes(text));
    setUser(filterUser);
  }, []);

  const shuffle = () => {
    setUser((user) => [...user].sort(() => Math.random() - 0.5));
  };

  return (
    <>
      <h1>App Component</h1>
      <SubInput onChangeHandler={onChangeHandler} />
      <button onClick={shuffle}>shuffle</button>
      <ul>
        {user.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </>
  );
};
export default App;

subInput.tsx

import React from "react";

const SubInput = ({
  onChangeHandler,
}: {
  onChangeHandler: (text: string) => void;
}) => {
  console.log("SubInput rendered");
  return (
    <>
      <input
        type="text"
        onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
          onChangeHandler(e.target.value);
        }}
      />
    </>
  );
};
export default React.memo(SubInput);


useMemo

useMemo 훅은 값을 메모이제이션 하는 훅입니다. 여기서 메모이제이션이란, 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장하여 사용함으로써 동일한 계산의 반복 수행을 제거하여 성능 향상을 도모하는 기술을 말합니다.

문법

const cachedValue = useMemo(calculateValue, dependencies)

예제

ex1

import React, { useState } from "react";

const App = () => {
  const [input, setInput] = useState("");
  const [todo, setTodo] = useState<string[]>([]);
  const todoLength = () => {
    console.log("todoLength");
    return todo.length;
  };
  const dummy = () => {
    setTodo([...todo, input]);
  };
  return (
    <div>
      <h1>Todo Length : {todoLength()}</h1>
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
      />
      <button onClick={dummy}>Dummy Todo</button>
      <pre>{JSON.stringify(todo)}</pre>
    </div>
  );
};

export default App;

app.tsx

import { useMemo, useState } from "react";
import { initialItems } from "./lib/utils";
const App = () => {
  const [count, setCount] = useState(0);

  // 불필요한 연산을 만들어준 것
  // 3000만개의 배열 데이터를 렌더링 마다 재생성하고 있음
  const [items] = useState(initialItems);

  const selectItems = useMemo(() => items.find((item) => item.selected), []);

  return (
    <>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount((prevCount) => prevCount + 1)}>
        증가
      </button>
      <p>{selectItems?.id}</p>
    </>
  );
};
export default App;

utils.ts

export const initialItems = new Array(29_999_999).fill(0).map((_, i) => {
  return {
    id: i,
    selected: i === 29_999_998,
  };
});

React.memo

React.memo 함수는 컴포넌트를 메모이제이션 할 때 사용합니다.

문법

const MemoizedComponent = React.memo(SomeComponent, arePropsEqual?)

예제

import { useCallback, useState } from "react";
import Header from "./components/Header";
import Footer from "./components/Footer";

const App = () => {
  const [input, setInput] = useState("");
  const [input2, setInput2] = useState("");
  const changeHandler = useCallback(() => {
    console.log("change");
  }, []);
  return (
    <div>
      <Header input={input} changeHandler={changeHandler} />
      <Footer input={input2} changeHandler={changeHandler} />
      <br />
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
      />
      <br />
      <input
        type="text"
        value={input2}
        onChange={(e) => setInput2(e.target.value)}
      />
    </div>
  );
};

export default App;

 


useReducer

조금 더 복잡한 상태 관리를 할 때 사용하는 리액트 훅입니다. 넓은 범위에서 useState 훅과 비슷합니다.

문법

const [state, dispatch] = useReducer(reducer, initialState);

예제

ex1) non-reducer

import { useState } from "react";
import "./Counter.css";
const Counter = () => {
  const [counter, setCounter] = useState(0);
  return (
    <div className="counter">
      <h1>{counter}</h1>
      <div>
        <button onClick={() => setCounter((count) => (count -= 1))}>-</button>
        <button onClick={() => setCounter((count) => (count += 1))}>+</button>
      </div>
    </div>
  );
};

export default Counter;
.counter {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
}

.counter h1 {
  font-size: 50px;
}

.counter button {
  width: 100px;
  height: 50px;
  background: none;
  margin-right: 10px;
  font-size: 30px;
}

.counter button:last-child {
  margin-right: 0;
}

ex2) useReducer

import { useReducer } from "react";
import "./Counter.css";

const reducer = (state: number, action: { type: string }) => {
  switch (action.type) {
    case "increament":
      return state + 1;
    case "decreament":
      return state - 1;
    default:
      return state;
  }
};

const Counter = () => {
  const [counter, dispatch] = useReducer(reducer, 0);
  return (
    <div className="counter">
      <h1>{counter}</h1>
      <div>
        <button onClick={() => dispatch({ type: "decreament" })}>-</button>
        <button onClick={() => dispatch({ type: "increament" })}>+</button>
      </div>
    </div>
  );
};

export default Counter;

 

 🔥 reduce 파일의 네이밍 규칙은 카멜 케이스를 따르는게 일반적입니다. ex) todoReducer.ts, createReducer.ts, authReducer.ts


[스나이퍼 팩토리 2기 과정]

본 학습 자료 및 코드는 수코딩님의 자료를 이용하였습니다. [수코딩(https://www.sucoding.kr)] 출처

728x90
반응형
LIST