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

相关推荐
程序员小寒4 分钟前
前端高频面试题之Vue(初、中级篇)
前端·javascript·vue.js
陈辛chenxin11 分钟前
软件测试大赛Web测试赛道工程化ai提示词大全
前端·可用性测试·测试覆盖率
沿着路走到底12 分钟前
python 判断与循环
java·前端·python
Code知行合壹15 分钟前
AJAX和Promise
前端·ajax
大菠萝学姐25 分钟前
基于springboot的旅游攻略网站设计与实现
前端·javascript·vue.js·spring boot·后端·spring·旅游
心随雨下37 分钟前
TypeScript中extends与implements的区别
前端·javascript·typescript
摇滚侠41 分钟前
Vue 项目实战《尚医通》,底部组件拆分与静态搭建,笔记05
前端·vue.js·笔记·vue
双向3342 分钟前
CANN训练营实战指南:从算子分析到核函数定义的完整开发流程
前端
caleb_52043 分钟前
vue cli的介绍
前端·javascript·vue.js
Swift社区44 分钟前
如何监测 Vue + GeoScene 项目中浏览器内存变化并优化性能
前端·javascript·vue.js