[React] ๐Ÿ“‹useMemo / ๐Ÿ”useCallback / ๐Ÿ—’React.memo

TATAยท2023๋…„ 3์›” 22์ผ
0

React

๋ชฉ๋ก ๋ณด๊ธฐ
19/32

๋ Œ๋”๋ง ์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ Hook์ธ
useMemo์™€ useCallback์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž๐Ÿ’จ

useMemo : ๊ฐ’์˜ ์žฌ์‚ฌ์šฉ์„ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” Hook
useCallback : ํ•จ์ˆ˜์˜ ์žฌ์‚ฌ์šฉ์„ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” Hook

๐Ÿ“‹ useMemo

์•„๋ž˜ ์ฝ”๋“œ์—์„œ ๋งŒ์•ฝ Calculator๊ฐ€ ๋ณต์žกํ•œ ์—ฐ์‚ฐ์„ ํ•˜๋Š” ํ•จ์ˆ˜๋ผ์„œ
๊ณ„์‚ฐ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒŒ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋ฉฐ, props๋กœ ๋ฐ›์€ value๊ฐ’์ด
๊ณ„์† ๋ฐ”๋€Œ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ๋ฉด, useMemo๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์ ํ™”๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

useMemo๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด value๊ฐ’์ด ์ด์ „๊ณผ ๋™์ผํ•œ ๊ฒฝ์šฐ์—๋Š”
์ด์ „ ๋ Œ๋”๋ง์˜ value๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์žฌํ™œ์šฉ
ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

(์ด์ „ ๊ฐ’๊ณผ ๊ฐ™์€๋ฐ, ๊ณ„์‚ฐ์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ํ•จ์ˆ˜๋ฅผ ๊ตณ์ด ๋‹ค์‹œ ํ˜ธ์ถœํ•  ํ•„์š”๊ฐ€ ์—†๊ฒŒ ๋จ)

/* useMemo ๊ฐ€์ ธ์˜ค๊ธฐ */
import { useMemo } from "react";

function Calculator({ value }) {

	const result = useMemo(() => calculate(value), [value]);

	return <>
      <div>
					{result}
      </div>
  </>;
}

๊ฐ™์€ ํ•จ์ˆ˜ ๋‚ด์— ์žˆ๋Š” ์ฒซ ๋ฒˆ์งธ input์ฐฝ์— ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜๋ฉด
๋ฆฌ๋ Œ๋”๋ง์ด ๋˜์–ด์„œ addํ•จ์ˆ˜๋„ ์žฌํ˜ธ์ถœ์ด ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ด๋•Œ useMemo๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด val1, val2์˜ ๊ฐ’์ด ๋ฐ”๋€” ๋•Œ์—๋งŒ
addํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜์–ด ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ๋ง‰์•„์ค€๋‹ค.

import React, { useState, useMemo } from "react";

const add = (num1, num2) => {
  console.log("์ˆซ์ž๊ฐ€ ๋“ค์–ด์˜ต๋‹ˆ๋‹ค.");
  return Number(num1) + Number(num2);
};

export default function App() {
  const [name, setName] = useState("");
  const [val1, setVal1] = useState(0);
  const [val2, setVal2] = useState(0);
  const answer = useMemo(() => add(val1, val2), [val1, val2]);
  // const answer = add(val1, val2);
  console.log(answer);

  return (
    <div>
      <input
        className="name-input"
        placeholder="์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”"
        value={name}
        type="text"
        onChange={(e) => setName(e.target.value)}
      />
      <input
        className="value-input"
        placeholder="์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”"
        value={val1}
        type="number"
        onChange={(e) => setVal1(Number(e.target.value))}
      />
      <input
        className="value-input"
        placeholder="์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”"
        value={val2}
        type="number"
        onChange={(e) => setVal2(Number(e.target.value))}
      />
      <div>{answer}</div>
    </div>
  );
}

๐Ÿ” useCallback

์‚ฌ์šฉ๋ฒ•์€ useMemo์™€ ๋น„์Šทํ•˜๋‹ค.
๋‹ค๋ฅธ ์ ์ด ์žˆ๋‹ค๋ฉด ๊ฐ’์ด ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜๋ฅผ ์žฌํ™œ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ.

useCallback์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ทธ ํ•จ์ˆ˜๊ฐ€ ์˜์กดํ•˜๋Š” ๊ฐ’๋“ค์ด
๋ฐ”๋€Œ์ง€ ์•Š๋Š” ํ•œ ๊ธฐ์กด ํ•จ์ˆ˜๋ฅผ ๊ณ„์†ํ•ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

But, ์ด๋ ‡๊ฒŒ ๋‹จ์ˆœํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด useCallback์„
์‚ฌ์šฉํ•˜๋ฉด, ํฐ ์˜๋ฏธ๊ฐ€ ์—†๊ฑฐ๋‚˜ ์˜คํžˆ๋ ค ๋” ์†ํ•ด์ผ ์ˆ˜ ์žˆ๋‹ค.

โ“๊ทธ๋Ÿผ ์–ธ์ œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์„๊นŒ?
useCallback์„ ์ด์šฉํ•ด ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ์ €์žฅํ•ด์„œ ๋‹ค์‹œ ์‚ฌ์šฉํ•˜๋ฉด,
ํ•จ์ˆ˜์˜ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ ๊ฐ’์„ ์ €์žฅํ–ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค.
๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ๋„˜๊ธฐ๊ฑฐ๋‚˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜
props๋กœ ๋„˜๊ธธ ๋•Œ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.

/* useCallback ๊ฐ€์ ธ์˜ค๊ธฐ */
import React, { useCallback } from "react";

function Calculator({ x, y }) {

    // x์™€ y๊ฐ’์ด ๋™์ผํ•˜๋‹ค๋ฉด ๋‹ค์Œ ๋ Œ๋”๋ง ๋•Œ ์ด ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ์‚ฌ์šฉ
    const add = useCallback(() => x + y, [x, y]);
  

	return <>
      <div>
					{add()}
      </div>
  </>;
}

๋‹คํฌ ๋ชจ๋“œ๋ฅผ ๋ˆŒ๋ €์„ ๋•Œ getItemsํ•จ์ˆ˜๊ฐ€ ๊ณ„์† ํ˜ธ์ถœ๋  ํ•„์š”๋Š” ์—†๋‹ค.
๋˜ํ•œ ์ž์‹์—๊ฒŒ๋„ getItems๋ฅผ ๋„˜๊ฒจ์ฃผ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—,
useCallback์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ๋ง‰์•„์ค„ ์ˆ˜ ์žˆ๋‹ค.

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

export default function App() {
  const [input, setInput] = useState(1);
  const [light, setLight] = useState(true);

  const theme = {
    backgroundColor: light ? "White" : "grey",
    color: light ? "grey" : "white"
  };

  const getItems = useCallback(() => {
    return [input + 10, input + 100];
  }, [input]);

  const handleChange = (event) => {
    if (Number(event.target.value)) {
      setInput(Number(event.target.value));
    }
  };

  return (
    <>
      <div style={theme} className="wall-paper">
        <input
          type="number"
          className="input"
          value={input}
          onChange={handleChange}
        />
        <button
          className={(light ? "light" : "dark") + " button"}
          onClick={() => setLight((prevLight) => !prevLight)}
        >
          {light ? "dark mode" : "light mode"}
        </button>
        <List getItems={getItems} />
      </div>
    </>
  );
}

-------
/* ์ž์‹ ์ปดํฌ๋„ŒํŠธ */
import { useState, useEffect } from "react";

function List({ getItems }) {
  const [items, setItems] = useState([]);

  useEffect(() => {
    console.log("์•„์ดํ…œ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.");
    setItems(getItems());
  }, [getItems]);

  return (
    <div>
      {items.map((item) => (
        <div key={item}>{item}</div>
      ))}
    </div>
  );
}

export default List;

๐Ÿ—’ React.memo

React์—์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” HOC(Higher Order Component)์ด๋‹ค.
React.memo๋Š” ์ปดํฌ๋„ŒํŠธ์˜ props๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ๋งŒ ๋‹ค์‹œ ๋ Œ๋”๋ง ํ•œ๋‹ค.

(useState, useReducer, useContext ํ›…์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด,
state๋‚˜ context๊ฐ€ ๋ณ€ํ•  ๋•Œ ๋‹ค์‹œ ๋ Œ๋”๋ง์ด ๋œ๋‹ค.)

import React from "react";

const Child = React.memo((props) => {
  /* props๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ Œ๋”๋ง */
})



๐Ÿ‘‰ React(๊ณต์‹๋ฌธ์„œ1) - useMemo ๏ผ‹ useCallback ๏ผ‹ React.memo
๐Ÿ‘‰ React(๊ณต์‹๋ฌธ์„œ2) - useMemo ๏ผ‹ useCallback

profile
๐Ÿพ

0๊ฐœ์˜ ๋Œ“๊ธ€