useMemo 是 React 提供的内置 Hooks,主要作用就是缓存,如果依赖项没有变化,Memo 方法不会再次执行,计算量比较高的方法可以使用,从而提高用户体验。本文将通过一个例子跟踪 Memo 的创建、更新流程。
//App.js
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
>
);
}
//TodoList.js
import List from './List.js';
import { filterTodos } from './utils.js'
import {useMemo} from 'react'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
return (
Note: List
is artificially slowed down!
);
}
/*
* filename List.js
*
*/
import { memo } from 'react';
const List = memo(function List({ items }) {
console.log('[ARTIFICIALLY SLOW] Rendering
with ' + items.length + ' items');
let startTime = performance.now();
while (performance.now() - startTime < 500) {
// Do nothing for 500 ms to emulate extremely slow code
}
return (
{items.map(item => (
-
{item.completed ?
{item.text} :
item.text
}
))}
);
});
export default List;
/*
* filename utils.js
*
*/
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
首次进入,运行并绑定结果。
进入绑定逻辑,可以看到 nextCreate 在这里运行,返回结果和依赖项。
点击 active 按钮,进入memo 更新流程
如果依赖没有变化,直接返回,有变化运行函数并更新。
Memo 的值也是存在 FiberNode 的memoizedState 属性中
Memo 的原理是根据依赖项变化判断是否更新,数据存在 FiberNode 上进行集中控制,更新时从组件的 FiberNode 上获取上次记录的状态并与本次的状态进行比较,并根据比较结果进行相应的处理。