# 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

Thanks for reading my blog post! Feel free to check out my other posts or contact me via the social links in the footer.


More Posts

Comments