React UseMemo源码分析

useMemo

useMemo 是 React 提供的内置 Hooks,主要作用就是缓存,如果依赖项没有变化,Memo 方法不会再次执行,计算量比较高的方法可以使用,从而提高用户体验。本文将通过一个例子跟踪 Memo 的创建、更新流程。

App.js

复制代码
//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 (
    <>
      <button onClick={() => setTab('all')}>
        All
      </button>
      <button onClick={() => setTab('active')}>
        Active
      </button>
      <button onClick={() => setTab('completed')}>
        Completed
      </button>
      <br />
      <label>
        <input
          type="checkbox"
          checked={isDark}
          onChange={e => setIsDark(e.target.checked)}
        />
        Dark mode
      </label>
      <hr />
      <TodoList
        todos={todos}
        tab={tab}
        theme={isDark ? 'dark' : 'light'}
      />
    </>
  );
}

//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 (
    <div className={theme}>
      <p><b>Note: <code>List</code> is artificially slowed down!</b></p>
      <List items={visibleTodos} />
    </div>
  );
}


/*
 *  filename List.js
 * 
 */
import { memo } from 'react';

const List = memo(function List({ items }) {
  console.log('[ARTIFICIALLY SLOW] Rendering <List /> with ' + items.length + ' items');
  let startTime = performance.now();
  while (performance.now() - startTime < 500) {
    // Do nothing for 500 ms to emulate extremely slow code
  }

  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>
          {item.completed ?
            <s>{item.text}</s> :
            item.text
          }
        </li>
      ))}
    </ul>
  );
});

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 上获取上次记录的状态并与本次的状态进行比较,并根据比较结果进行相应的处理。

相关推荐
Kimser2 分钟前
基于 VxeTable 的高级表格选择组件
前端·vue.js
摸着石头过河的石头5 分钟前
JavaScript 防抖与节流:提升应用性能的两大利器
前端·javascript
酸菜土狗7 分钟前
让 ECharts 图表跟随容器自动放大缩小
前端
_大学牲11 分钟前
FuncAvatar: 你的头像氛围感神器 🤥🤥🤥
前端·javascript·程序员
葡萄城技术团队21 分钟前
SpreadJS 性能飙升秘籍:底层优化技术深度拆解
前端
brzhang22 分钟前
我且问你,如果有人用 AI 抄你的产品,爱卿又当如何应对?
前端·后端·架构
533_1 小时前
[element-ui] el-tree 组件鼠标双击事件
前端·javascript·vue.js
KIKIiiiiiiii1 小时前
微信个人号开发中如何高效实现API二次开发
java·前端·python·微信
日月之行_1 小时前
Vite+:企业级前端构建的新选择
前端
山顶听风1 小时前
Flask应用改用Waitress运行
前端·笔记·python·flask