react-useMemo

2 min read
Table of Contents

主要作用

核心概念

注意事项

示例代码

  1. 组件重新渲染的例子

提示: 修改名字会触发 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>
  )
}
  1. 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

后记

My avatar

感谢你读到这里。

这座小站更像一份持续维护的“终端笔记”:记录我解决问题的过程,也记录走过的弯路。

如果这篇内容对你有一点点帮助:

  • 点个赞 / 收藏一下,方便你下次回来继续翻
  • 欢迎在评论区补充你的做法(或者指出我的疏漏)
  • 想持续收到更新:可以订阅 RSS(在页面底部)

我们下篇见。


More Posts

评论

评论 (0)

请先登录后再发表评论

加载中...