前言
组件中状态(State)发生改变会导致该组件重新渲染,其中的子组件也会被重新渲染。如果子组件中并未使用该状态(State),重复渲染会导致无效的性能损耗。
在阻止重新渲染这个需求的基础上,诞生了memo函数,memo是react的一种缓存技术,这个函数可以检测从父组件接收的props,并且在父组件改变state的时候比对这个state是否是本组件在使用,如果不是,则不会重新渲染子组件。
基本使用
使用 memo 将组件包装起来,以获得该组件的一个 记忆化 版本。通常情况下,只要该组件的 props 没有改变,这个记忆化版本就不会在其父组件重新渲染时重新渲染。
import { memo } from “react”
memo(Component, arePropsEqual?)
参数
Component:要进行记忆化的组件。memo不会修改该组件,而是返回一个新的、记忆化的组件。它接受任何有效的 React 组件,包括函数组件和forwardRef组件。- 可选参数
arePropsEqual:一个函数,接受两个参数:组件的前一个 props 和新的 props。如果旧的和新的 props 相等,即组件使用新的 props 渲染的输出和表现与旧的 props 完全相同,则它应该返回true。否则返回false。通常情况下,你不需要指定此函数。默认情况下,React 将使用Object.is比较每个 prop。
返回值
memo 返回一个新的 React 组件。它的行为与提供给 memo 的组件相同,只是当它的父组件重新渲染时 React 不会总是重新渲染它,除非它的 props 发生了变化。
案例
未使用memo
Head组件中并未使用count,但setCount后,也发生了重新渲染。
import React, { useState } from "react"
function Head() {
return <div>Head,{Math.random()}</div>
}
function App() {
const [count, setCount] = useState(1)
return (
<>
<div>App,count={count}</div>
<button
onClick={() => {
setCount(count + 1)
}}
>
添加
</button>
<Head></Head>
</>
)
}
点击添加前

点击后,可以发现setState导致子组件也重新渲染。

如果当子组件中逻辑复杂或者父组件会频繁重新渲染,就会带来性能消耗,可以使用memo来对此进行优化。
使用memo
import React, { useState, memo } from "react"
const Head = memo(function Head() {
return <div>Head,{Math.random()}</div>
})
function App() {
const [count, setCount] = useState(1)
return (
<>
<div>App,count={count}</div>
<button
onClick={() => {
setCount(count + 1)
}}
>
添加
</button>
<Head></Head>
</>
)
}
点击前

多次点击发现,子组件并不会重新渲染

如果子组件props也接收了count这个状态,那么还会再次渲染。
注意
当props为复杂数据类型时,setCount后,App组件重新渲染,此时list的地址发生改变,由于Object.is对比的是两个复杂数据类型的地址,所以子组件的props会被视为已发生改变,导致再次渲染,此时需要使用 useMemo 对计算结果进行缓存。
import React, { useState, memo } from "react"
const Head = memo(function Head() {
return <div>Head,{Math.random()}</div>
})
function App() {
const [count, setCount] = useState(1)
const [msg, setMsg] = useState("Hello World")
const list = [msg.toLowerCase(), msg.toUpperCase()]
return (
<>
<div>App,count={count}</div>
<button
onClick={() => {
setCount(count + 1)
}}
>
添加
</button>
<Head list={list}></Head>
</>
)
}
使用 useMemo 缓存
import React, { useState, memo, useMemo } from "react"
const Head = memo(function Head() {
return <div>Head,{Math.random()}</div>
})
function App() {
const [count, setCount] = useState(1)
const [msg, setMsg] = useState("Hello World")
const list = useMemo(()=>[msg.toLowerCase(), msg.toUpperCase()],[msg])
return (
<>
<div>App,count={count}</div>
<button
onClick={() => {
setCount(count + 1)
}}
>
添加
</button>
<Head list={list}></Head>
</>
)
}
useMemo可以将结果进行缓存,其第一个参数的返回值为需要缓存的数据,第二个参数是一个依赖数组,只有依赖项发生改变时,才会重新计算,否则一直使用缓存值。
当props为函数时,useMemo的写法可读性较差,可以使用useCallback对函数进行缓存。
import React, { useState, memo, useMemo, useCallback } from "react"
const Head = memo(function Head() {
return <div>Head,{Math.random()}</div>
})
function App() {
const [count, setCount] = useState(1)
const [msg, setMsg] = useState("Hello World")
// 使用useMemo
const fn = useMemo(
() => () => {
console.log("fn")
},
[msg]
)
// 使用useCallback
const fn = useCallback(() => {
console.log("fn")
}, [msg])
return (
<>
<div>App,count={count}</div>
<button
onClick={() => {
setCount(count + 1)
}}
>
添加
</button>
<Head fn={fn}></Head>
</>
)
}
总结
被memo函数处理过的组件只有当本身的props改变之后才会重新渲染。
memo源码
源码
function memo(type, compare) {
// 核心代码
return {
$$typeof: REACT_MEMO_TYPE,
type: type,
compare: compare === undefined ? null : compare
};
}
memo 的源码比较简单,在返回一个记忆化组件的同时,给组件添加 $$typeof 标记。在创建 Fiber 时,会根据 $$typeof 的值,给 fiber.tag 赋值为 MemoComponent(MemoComponent=14)。
工作流
当 state(可以是任何组件的 state)改变时,React 会从根节点开始遍历 Fiber 树,在这个过程中会对每一个 Fiber 节点调用 beginWork 方法。当 tag 为 MemoComponent 时,会调用 updateMemoComponent 方法。当我们在使用 memo 没有传入第二个参数时,会调用默认的 shallowEqual 对 prevProps 和 nextProps 进行浅比较,当结果为 true 时,组件就不会重新渲染。
function beginWork(...) {
// ...
switch (workInProgress.tag) {
case MemoComponent: {
return updateMemoComponent(...);
}
}
}
function updateMemoComponent(...) {
// ...
if (updateExpirationTime < renderExpirationTime) {
// Default to shallow comparison
var compare = Component.compare;
compare = compare !== null ? compare : shallowEqual;
if (compare(prevProps, nextProps) && current$$1.ref === workInProgress.ref) {
return bailoutOnAlreadyFinishedWork(...);
}
}
var newChild = createWorkInProgress(...);
return newChild;
}
文章介绍了React中的memo函数,一种用于优化组件渲染性能的缓存技术。当子组件的props不依赖于父组件的状态时,memo会避免不必要的子组件渲染,降低性能消耗。同时提到了useMemo和useCallback的使用方法以及它们在复杂数据类型和函数场景下的应用。
461

被折叠的 条评论
为什么被折叠?



