React’s useMemo Hook to memoize a functions return value(s) and to run a function only if its dependencies have changed
基本使用
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
返回一个 memoized 值。
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
记住,传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。
如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。
示例1
import React from "react";const users = [{ id: "a", name: "Robin" }, { id: "b", name: "Dennis" }];const List = ({ list }) => {return (<ul>{list.map(item => (<ListItem key={item.id} item={item} />))}</ul>);};const ListItem = ({ item }) => {return <li>{item.name}</li>;};const App = () => {const [text, setText] = React.useState("");const [search, setSearch] = React.useState("");const handleText = event => {setText(event.target.value);};const handleSearch = () => {setSearch(text);};const filteredUsers = users.filter(user => {console.log("Filter function is running ...");return user.name.toLowerCase().includes(search.toLowerCase());});return (<div><input type="text" value={text} onChange={handleText} /><button type="button" onClick={handleSearch}>Search</button><List list={filteredUsers} /></div>);};export default App;
上面的例子中,在input中输入的时候,App的状态text会改变,App就会重新渲染,重新define filteredUsers,就会打印 Filter function is running …

可以看到只有users变化的时候,才会触发filteredUsers
示例2
假设以下场景,父组件在调用子组件时传递 info 对象属性,点击父组件按钮时,发现控制台会打印出子组件被渲染的信息。
import React, { memo, useState } from 'react';// 子组件const ChildComp = (info:{info:{name: string, age: number}}) => {console.log('ChildComp...');return (<div>ChildComp...</div>);};const MemoChildComp = memo(ChildComp);// 父组件const Parent = () => {const [count, setCount] = useState(0);const [name] = useState('jack');const [age] = useState(11);const info = { name, age };return (<div className="App"><div>hello world {count}</div><div onClick={() => { setCount(count => count + 1); }}>点击增加</div><MemoChildComp info={info}/></div>);};export default Parent;
分析原因:
点击父组件按钮,触发父组件重新渲染;父组件渲染,const info = { name, age } 一行会重新生成一个新对象,导致传递给子组件的 info 属性值变化,进而导致子组件重新渲染。
解决:
使用 useMemo 将对象属性包一层,useMemo 有两个参数:
- 第一个参数是个函数,返回的对象指向同一个引用,不会创建新对象;
 - 第二个参数是个数组,只有数组中的变量改变时,第一个参数的函数才会返回一个新的对象。
 
import React, { memo, useMemo, useState } from 'react';// 子组件const ChildComp = (info:{info:{name: string, age: number}}) => {console.log('ChildComp...');return (<div>ChildComp...</div>);};const MemoChildComp = memo(ChildComp);// 父组件const Parent = () => {const [count, setCount] = useState(0);const [name] = useState('jack');const [age] = useState(11);// 使用 useMemo 将对象属性包一层const info = useMemo(() => ({ name, age }), [name, age]);return (<div className="App"><div>hello world {count}</div><div onClick={() => { setCount(count => count + 1); }}>点击增加</div><MemoChildComp info={info}/></div>);};export default Parent;
完整代码见https://stackblitz.com/edit/react-kkxeqs
使用场景
- 优化函数返回值
 
Reference:
https://www.robinwieruch.de/react-usememo-hook
https://mp.weixin.qq.com/s/fF-H3Lr0aP3Ld8jfJrrwQg
