# react-useMemo
2 min read
Table of Contents
主要作用
核心概念
注意事项
示例代码
- 组件重新渲染的例子
提示: 修改名字会触发 useMemo 重新计算,但点击计数器不会(打开控制台查看日志)
JOHN
年龄: 20
电话: 8008208820DHC
import React, { useMemo, useState } from 'react'
const UserCard = (props: {
user: {
name: string
age: number
phone: string
}
}) => {
// 使用 useMemo 缓存格式化后的用户信息
const formattedUser = useMemo(() => {
console.log('重新计算用户信息')
return {
name: props.user.name.toUpperCase(),
age: `年龄: ${props.user.age}`,
phone: `电话: ${props.user.phone}`,
}
}, [props.user.name, props.user.age, props.user.phone])
return (
<div className="flex flex-col items-start justify-center gap-2 h-40 w-80 bg-[#242424] text-white rounded-xl p-4">
<div>{formattedUser.name}</div>
<div>{formattedUser.age}</div>
<div>{formattedUser.phone}</div>
</div>
)
}
export default function App() {
const [user, setUser] = useState({
name: 'John',
age: 20,
phone: '8008208820DHC',
})
const [input, setInput] = useState('')
const [counter, setCounter] = useState(0)
const changeUser = () => {
setUser({
...user,
name: input,
})
}
return (
<div className="flex flex-col gap-4">
<div className="flex gap-2 items-center">
<button
onClick={changeUser}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
修改用户
</button>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="输入新名字"
className="px-3 py-2 border border-gray-300 rounded"
/>
<button
onClick={() => setCounter(counter + 1)}
className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
>
计数器: {counter}
</button>
</div>
<p className="text-sm text-gray-600">
提示: 修改名字会触发 useMemo 重新计算,但点击计数器不会(打开控制台查看日志)
</p>
<UserCard user={user} />
</div>
)
}- useMemo 缓存计算结果的例子
购物车
| 商品 | 单价 | 数量 | 总价 | 操作 |
|---|---|---|---|---|
| 小满(只) | 100 | 1 | 100 | |
| 中满(只) | 200 | 1 | 200 | |
| 大满(只) | 300 | 1 | 300 |
总价: 600
import { useMemo, useReducer, useState } from 'react'
const initData = [
{ name: '小满(只)', price: 100, count: 1, id: 1, isEdit: false },
{ name: '中满(只)', price: 200, count: 1, id: 2, isEdit: false },
{ name: '大满(只)', price: 300, count: 1, id: 3, isEdit: false },
]
type Data = typeof initData
const reducer = (
state: Data,
action: {
type: 'add' | 'sub' | 'delete' | 'edit' | 'blur' | 'update_name'
id: number
newName?: string
},
) => {
const item = state.find((i) => i.id === action.id)!
switch (action.type) {
case 'add':
item.count++
return [...state]
case 'sub':
item.count = Math.max(0, item.count - 1)
return [...state]
case 'delete':
return state.filter((item) => item.id !== action.id)
case 'edit':
item.isEdit = true
return [...state]
case 'update_name':
item.name = action.newName!
return [...state]
case 'blur':
item.isEdit = false
return [...state]
}
return state
}
function App() {
const [data, dispatch] = useReducer(reducer, initData)
const total = useMemo(() => {
return data.reduce((acc, pre) => {
return acc + pre.price * pre.count
}, 0)
}, [data])
return (
<div className="h-120 w-full bg-[#242424]">
<h1 className="text-xl text-red-200 w-full text-center p-5">购物车</h1>
<table className="w-4xl text-white mx-auto border border-gray-600">
<thead>
<tr>
<th className="py-4">商品</th>
<th className="py-4">单价</th>
<th className="py-4">数量</th>
<th className="py-4">总价</th>
<th className="py-4">操作</th>
</tr>
{data.map((item) => {
return (
<tr key={item.id}>
<td className="py-4" align="center">
{item.isEdit ? (
<input
value={item.name}
onChange={(e) =>
dispatch({
type: 'update_name',
id: item.id,
newName: e.currentTarget.value,
})
}
onBlur={() =>
dispatch({
type: 'blur',
id: item.id,
})
}
/>
) : (
item.name
)}
</td>
<td className="py-4" align="center">
{item.price}
</td>
<td
className="py-4 flex items-center justify-center gap-2"
align="center"
>
<button
className="p-4 bg-green-500 cursor-pointer"
onClick={() =>
dispatch({
type: 'add',
id: item.id,
})
}
>
+
</button>
<div>{item.count}</div>
<button
className="p-4 bg-red-500 cursor-pointer"
onClick={() =>
dispatch({
type: 'sub',
id: item.id,
})
}
>
-
</button>
</td>
<td className="py-4" align="center">
{item.price * item.count}
</td>
<td
className="py-4 flex gap-5 items-center justify-center"
align="center"
>
<button
className="cursor-pointer"
onClick={() =>
dispatch({
type: 'edit',
id: item.id,
})
}
>
编辑
</button>
<button
className="cursor-pointer"
onClick={() =>
dispatch({
type: 'delete',
id: item.id,
})
}
>
删除
</button>
</td>
</tr>
)
})}
</thead>
</table>
<div className="text-white text-center p-5">
总价: {total}
</div>
</div>
)
}
export default App