🧩 React.memo 小练习题 + 参考答案
练习 1:基本使用
题目:
创建一个父组件,里面有两个状态:count
和 message
。
父组件渲染一个子组件 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
,接收 name
和 age
。
要求:只有 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>
);
}