React.memo 小练习题 + 参考答案

发布于:2025-09-13 ⋅ 阅读:(14) ⋅ 点赞:(0)

🧩 React.memo 小练习题 + 参考答案


练习 1:基本使用

题目
创建一个父组件,里面有两个状态:countmessage
父组件渲染一个子组件 Message,它只接收 message 作为 props,并在渲染时打印 "Message render"
点击按钮改变 count,观察子组件是否重新渲染。
React.memo 优化 Message,观察差异。

答案代码:

import React, { useState } from "react";

const Message = React.memo(({ message }) => {
  console.log("Message render");
  return <h2>{message}</h2>;
});

export default function App() {
  const [count, setCount] = useState(0);
  const [message, setMessage] = useState("Hello");

  return (
    <div>
      <h1>Count: {count}</h1>
      <Message message={message} />
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <button onClick={() => setMessage(message + "!")}>Change Message</button>
    </div>
  );
}

练习 2:浅比较的局限性

题目
父组件中维护一个 user = { name: "Tom" },把它作为 props 传给 UserCard
点击按钮时执行 setUser({ name: "Tom" })
观察即使数据没变,子组件还是会重新渲染。
尝试用 useMemo 固定对象引用,避免重新渲染。

答案代码:

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

const UserCard = React.memo(({ user }) => {
  console.log("UserCard render");
  return <h2>User: {user.name}</h2>;
});

export default function App() {
  const [count, setCount] = useState(0);

  // ❌ 没优化
  // const user = { name: "Tom" };

  // ✅ 优化
  const user = useMemo(() => ({ name: "Tom" }), []);

  return (
    <div>
      <h1>Count: {count}</h1>
      <UserCard user={user} />
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
    </div>
  );
}

练习 3:结合 useCallback

题目
父组件定义一个回调函数 handleClick,传给子组件 ButtonChild
不使用 useCallback 时,子组件每次都会重新渲染。
useCallback 包装后,子组件不再重复渲染。

答案代码:

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

const ButtonChild = React.memo(({ onClick }) => {
  console.log("ButtonChild render");
  return <button onClick={onClick}>Child Button</button>;
});

export default function App() {
  const [count, setCount] = useState(0);

  // ❌ 每次 App 渲染都会生成新函数
  // const handleClick = () => console.log("clicked");

  // ✅ useCallback 固定引用
  const handleClick = useCallback(() => console.log("clicked"), []);

  return (
    <div>
      <h1>Count: {count}</h1>
      <ButtonChild onClick={handleClick} />
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
    </div>
  );
}

练习 4:列表优化

题目
创建一个父组件,渲染 100 个用户,每个用户是一个子组件 UserRow
点击“刷新时间”按钮,观察整个列表是否重新渲染。
UserRow 添加 React.memo,避免不必要的渲染。

答案代码:

import React, { useState } from "react";

const UserRow = React.memo(({ name }) => {
  console.log("Render UserRow:", name);
  return <li>{name}</li>;
});

export default function App() {
  const [time, setTime] = useState(Date.now());

  const users = Array.from({ length: 100 }, (_, i) => `User ${i + 1}`);

  return (
    <div>
      <h1>Time: {time}</h1>
      <button onClick={() => setTime(Date.now())}>Refresh Time</button>
      <ul>
        {users.map((u) => (
          <UserRow key={u} name={u} />
        ))}
      </ul>
    </div>
  );
}

练习 5:自定义比较函数

题目
创建一个子组件 Profile,接收 nameage
要求:只有 age 改变时才重新渲染。
使用 React.memo 的第二个参数实现。

答案代码:

import React, { useState } from "react";

const Profile = React.memo(
  ({ name, age }) => {
    console.log("Profile render");
    return (
      <div>
        <h2>{name}</h2>
        <p>Age: {age}</p>
      </div>
    );
  },
  (prevProps, nextProps) => {
    return prevProps.age === nextProps.age; // 只在 age 改变时重新渲染
  }
);

export default function App() {
  const [name, setName] = useState("Tom");
  const [age, setAge] = useState(20);

  return (
    <div>
      <Profile name={name} age={age} />
      <button onClick={() => setName(name + "!")}>Change Name</button>
      <button onClick={() => setAge(age + 1)}>Increment Age</button>
    </div>
  );
}

练习 6:性能对比(进阶)

题目
渲染一个 5000 行的大列表,每一行是 ItemRow
点击按钮时更新一个无关的状态,看看没有 React.memo 时所有子组件是否重新渲染。
加上 React.memo 后,对比性能变化。

答案代码:

import React, { useState } from "react";

const ItemRow = React.memo(({ value }) => {
  console.log("Render:", value);
  return <div>{value}</div>;
});

export default function App() {
  const [tick, setTick] = useState(0);
  const items = Array.from({ length: 5000 }, (_, i) => `Item ${i}`);

  return (
    <div>
      <h1>Tick: {tick}</h1>
      <button onClick={() => setTick(tick + 1)}>Update Tick</button>
      {items.map((item) => (
        <ItemRow key={item} value={item} />
      ))}
    </div>
  );
}