React / React.memo

React.memo

React апп томрох тусам гүйцэтгэл чухал болж ирдэг. Энэ хичээлд React.memo ашиглан component-ийг хэрэггүй дахин рендерлэхээс хэрхэн хамгаалахыг сурна. Эхлэгчдэд хэцүү сэдэв мэт санагдаж болох ч жишээнүүдийг дагаж сурвал маш хялбар!

Яагаад React.memo хэрэгтэй вэ?

React-д parent component рендерлэгдэх бүрт түүний бүх child component-ууд ч дахин рендерлэгддэг — тэдний props өөрчлөгдсөн эсэхийг үл харгалзан. Энэ нь жижиг аппад асуудал биш боловч том аппад гүйцэтгэлийг удаашруулдаг.

jsx
function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>+1</button>
      {/* count өөрчлөгдөх бүрт Child ч рендерлэгдэнэ */}
      <Child name="Болд" />
    </div>
  );
}

function Child({ name }) {
  console.log("Child рендерлэгдлээ");
  return <p>Сайн уу, {name}!</p>;
}

Дээрх жишээнд Child-д дамжигч name prop хэзээ ч өөрчлөгддөггүй. Гэсэн ч count өөрчлөгдөх бүрт Child дахин рендерлэгдэнэ. Энэ бол хэрэггүй ажил.

React.memo ашиглах нь

React.memo нь component-ийг "memorize" хийдэг — props өөрчлөгдөөгүй бол дахин рендерлэхгүй.

jsx
import { memo } from "react";

// memo() дотор component-оо боож өгнө
const Child = memo(function Child({ name }) {
  console.log("Child рендерлэгдлээ");
  return <p>Сайн уу, {name}!</p>;
});

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>+1 ({count})</button>
      {/* name prop өөрчлөгдөөгүй тул Child дахин рендерлэгдэхгүй */}
      <Child name="Болд" />
    </div>
  );
}

Одоо count өөрчлөгдөхөд Child дахин рендерлэгдэхгүй — учир нь name="Болд" өөрчлөгдөөгүй байна.

React.memo хэзээ ашиглах вэ?

React.memo бүх газар хэрэглэх шаардлагагүй. Дараах нөхцөлд л хэрэглэх нь зүйтэй:

jsx
// ✅ Зөв — рендерлэх нь үнэтэй, props тогтмол
const HeavyChart = memo(function HeavyChart({ data }) {
  // их тооцоолол...
  return <canvas />;
});

// ✅ Зөв — жагсаалтын мөр, олон удаа рендерлэгдэнэ
const ListItem = memo(function ListItem({ item }) {
  return <li>{item.title}</li>;
});

// ❌ Хэрэггүй — props байнга өөрчлөгдөж байвал memo тус болохгүй
const Counter = memo(function Counter({ count }) {
  return <p>{count}</p>;
});

React.memo нь props-ийг гадаргуугаар харьцуулдаг (shallow comparison). Тиймээс object эсвэл array props байвал useMemo эсвэл useCallback-тай хослуулан хэрэглэх хэрэгтэй болдог — дараагийн хичээлүүдэд үүнийг сурна.

Жишээ: тооцоолол ихтэй жагсаалт

jsx
import { memo, useState } from "react";

// Энэ component-ийг memo-гоор хамгаална
const ProductCard = memo(function ProductCard({ product }) {
  console.log(`${product.name} рендерлэгдлээ`);
  return (
    <div style={{ border: "1px solid #ccc", padding: "8px", margin: "4px" }}>
      <h3>{product.name}</h3>
      <p>{product.price}₮</p>
    </div>
  );
});

export default function Shop() {
  const [search, setSearch] = useState("");
  const products = [
    { id: 1, name: "Гар утас", price: 800000 },
    { id: 2, name: "Зөөврийн компьютер", price: 2500000 },
    { id: 3, name: "Чихэвч", price: 150000 },
  ];

  return (
    <div>
      <input
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="Хайх..."
      />
      {/* search өөрчлөгдөхөд ProductCard-ууд дахин рендерлэгдэхгүй */}
      {products.map((p) => (
        <ProductCard key={p.id} product={p} />
      ))}
    </div>
  );
}

Console-д харвал ProductCard-ууд зөвхөн эхний рендерт л log хэвлэнэ — search бичих бүрт дахин хэвлэгдэхгүй. React.memo ажиллаж байна!

Дараагийн хичээлд:

useMemo hook-ийг сурна — component дотор хийгддэг үнэтэй тооцооллын үр дүнг хэрхэн санаж үлдэхийг ойлгоно. React.memo ба useMemo хоёрын ялгааг тодруулна.