理想是火,点燃熄灭的灯。
在优化代码和提高组件计算性能时推荐使用useMemo
当你有一个计算量比较大的值时,或者这个值的计算依赖某些 props/state,useMemo
可以在依赖没变的情况下跳过计算,直接复用上一次的结果。
示例说明
const MyComponent = ({ items }) => { const expensiveCalculation = (data) => { console.log("Calculating..."); return data.reduce((acc, item) => acc + item, 0); }; const total = useMemo(() => expensiveCalculation(items), [items]); return <div>Total: {total}</div>; };
如果 items
没变,即使组件重新渲染,expensiveCalculation
也不会重新执行。
注意着不是react原生提供的hook,而是ahooks库封装的
目的是用来简化和增强请求操作的体验。
与传统方法对比的示例:
useRequest
import { useRequest } from 'ahooks'; import { getUserInfo } from './services'; export default () => { const { data, error, loading } = useRequest(getUserInfo); if (loading) return <div>加载中...</div>; if (error) return <div>出错了</div>; return <div>用户名:{data.name}</div>; };
getUserInfo 通常是一个promise方法
import axios from 'axios'; export async function getUserInfo() { const response = await axios.get('/api/user/info'); return response.data; }
手动触发请求:
const { run, loading } = useRequest(fetchData, { manual: true }); <Button onClick={() => run(params)}>点击获取数据</Button>
手动触发(manual: true
):
传统写法
const [loading, setLoading] = useState(false); const [data, setData] = useState(null); useEffect(() => { setLoading(true); fetchData().then(res => { setData(res); }).finally(() => { setLoading(false); }); }, []);
用于子组件的缓存
默认情况下 当父组件重新渲染时,子组件也会重新渲染
但是如果这个子组件的复杂度很大,且依赖的父组件数据没有变化,则不需要重新渲染
就可以使用React.memo套一下,把子组件缓缓起来
const Parent = () => { const [count, setCount] = useState(0); console.log('👨👦 父组件渲染'); return ( <div> <button onClick={() => setCount(count + 1)}>加一</button> <Child name="小明" /> </div> ); }; const Child = React.memo(({ name }) => { console.log('👶 子组件渲染'); return <div>Hello {name}</div>; });
想要正确的使用react.memo 需要看传递的数据 或者方法
如果是方法 就必须使用useCallback来缓存方法
如果是数据类型的,且是引用类型的,就必须使用useMemo来缓存引用类型
(useCallback 似乎只有在配合React.memo时才有效果
不像useMemo 它似乎也可以单独的去缓存使用)
import React, { useState, useCallback, useMemo } from 'react'; const Child = React.memo(({ count, onClick, config }) => { console.log('👶 Child rendered'); return ( <div style={{ border: '1px solid #ccc', padding: 10 }}> <p>Count: {count}</p> <p>Theme: {config.theme}</p> <button onClick={onClick}>Click Me</button> </div> ); }); export default function App() { const [parentCount, setParentCount] = useState(0); // ✅ 原始值:直接传,不需要 useMemo const count = parentCount; // ✅ useCallback:缓存函数引用 const handleClick = useCallback(() => { console.log('Button clicked!'); }, []); // ✅ useMemo:缓存对象引用 const config = useMemo(() => ({ theme: 'dark' }), []); return ( <div> <h2>Parent Count: {parentCount}</h2> <button onClick={() => setParentCount(c => c + 1)}>+1 Parent Count</button> <Child count={count} onClick={handleClick} config={config} /> </div> ); }
多层嵌套里,useEffect
执行顺序依然是从最外层父组件开始,逐层往里依次执行。也就是说:
假设组件关系:A -> B -> C -> D
执行顺序是:
A useEffect B useEffect C useEffect D useEffect
卸载清理函数执行顺序是:
子组件先清理 → 一层层向上到父组件,也就是:
D 卸载清理函数 C 卸载清理函数 B 卸载清理函数 A 卸载清理函数
主要是配合redux来通过缓存仓库数据,提高性能
使用场景多个页面使用了同样的仓库中衍生出来的计算结果(派生数据)
希望把这个结果给缓存下来,避免重复的计算
A/B/C 三个页面都需要用到一个仓库的派生数据
如果先到A页面,如果仓库的源数据没有变化,那么就不会重复计算,直接返回上一次的派生数据结果
B/C页面就避免了重复的计算过程
代码示例
react 组件a 引用
const noPoachingCompany = useSelector(getNoPoachingCompany);
selector.js 声明
import { createSelector } from 'reselect'; const getCompany = (state) => state.model.companies; export const getNoPoachingCompany = createSelector( [getCompany], (companies) => { return companies.filter((c) => c.noPoaching); }, );
通过id匹配 仓库中存储的用户列表(举例10万个用户),获取名字 这种方式
第一种优化方法:使用reselect来进行匹配(如果传递的id重复出现,不会重复匹配计算,直接命中缓存,直接返回),但是有局限性,只能命中匹配上一次输入的id,无法记录多种数据
第二种优化方法:自己写一个匹配方法,没成功匹配后,将结果存为一份mapping格式的数据,如果匹配到多个或者单个,直接返回,不需要重复循环匹配
第三种优化方法:直接以mapping格式将用户表存入仓库,后续取值直接取值使用,不用循环匹配
最佳的优化方案是第三种,因为这种方式是O(1)(输入多少都一样快,常数级时间)
拓展
O(1)是直接访问 不涉及遍历
O(n)是遍历所有元素一变
O(n²)是每个元素都和每个元素比较
O(1):常数时间复杂度。
直接访问数据,不管数据多少,操作次数基本固定。
例子:数组通过索引访问 arr[5]
。
O(n):线性时间复杂度。
遍历所有元素,操作次数和数据量成正比。
例子:遍历数组求和。
O(n²):平方时间复杂度。
对每个元素,都要和其他所有元素进行比较,操作次数是数据量的平方。
例子:冒泡排序,两层嵌套循环比较数组中的每对元素。
作者: Bill 本文地址: http://biaoblog.cn/info?id=1749800783394
版权声明: 本文为原创文章,版权归 biaoblog 个人博客 所有,欢迎分享本文,转载请保留出处,谢谢!
上一篇:没有了