memo用来优化函数组件的重复渲染行为,当传入属性值都不变的情况下不会触发组件的重新渲染,否则就会触发组件的重新渲染;和类组件的PureComponent的功能是类似的;在hooks环境下,几乎所有组件都是函数式组件,我们使用memo的几率要比PureComponent高得多;
const ExpensiveComponent = React.memo(() => {
console.log('Expensive Component Rendered');
let total = 0;
for (let i = 0; i < 1000000000; i++) {
total += i;
}
return <div>Expensive Component</div>
})
export default ExpensiveComponent;
memo针对的是一个组件的渲染是否重复执行,而useMemo则定义了一段函数逻辑是否重复执行
本质都是用同样的算法来判定依赖是否发生改变,继而决定是否触发特定逻辑;(有很多这样的逻辑:输入和输出是对等的,相同的输入一定产生相同的输出,数学上称之为幂等)利用memo就可以避免不必要的重复计算,减少资源浪费。
所以严格来讲,不使用memo和useMemo不应该会导致你的业务逻辑发生变化(memo和useMemo只是用来做性能优化)
import { useEffect, useMemo } from "react";
function App () {
const [name, setName] = useState('');
const [age, setAge] = useState(null);
const [country, setCountry] = useState('');
const userType = useMemo(() => ({
underAge: age < 18 ? true : false,
citizen: country === 'USA' ? true : false
}), [age, country]);
useEffect(() => {
console.count("User type has changed");
}, [userType]);
console.log("Component re-rendered")
return (
<div>
<input type="text" value={name} onChange={e => setName(e.target.value)} />
<input type="number" value={age} onChange={e => setAge(e.target.value)} />
<input type="text" value={country} onChange={e => setCountry(e.target.value)} />
</div>
)
}
useMemo和useEffect的语法是一样的,第一个参数是要执行的逻辑函数,第二个参数是这个逻辑函数依赖的变量组成的数组,如果不传第二个参数,则useMemo的逻辑函数每次都执行,那useMemo的意义就不存在了。如果传入空数组,则只执行一次;
useMemo和useEffect的执行时机是不一致的:useEffect执行的是副作用,所以一定是在渲染之后执行的,useMemo是需要有返回值的,而返回值可以直接参与渲染的,所以useMemo是在渲染期间完成的,有这样一个一前一后的区别
userMemo和useCallback接受的参数都是一样的,第一个参数为回调函数,第二个参数是要依赖的数据;
共同点:只有在依赖数据发生变化后,才会重新计算结果,起到缓存的作用
区别:useMemo返回的是计算的结果值,用于缓存计算后的状态
useCallback返回的是函数,主要用来缓存函数,因为函数式组件中的state的变化都会导致整个组件被重新刷新(即使一些函数没有必要被刷新),此时用useCallback就会将函数进行缓存,减少渲染时的性能损耗;
import { useEffect } from "react";
export default function UseCallbackPage () {
const [count, setCount] = React.useState(0);
// 不加 useCallback,input框输入事件也会触发ChildMemo组件重新渲染;利用 useCallback缓存 addClick 函数,并添加 count 作为依赖项,可以解决子组件重新渲染问题
const handleClick = React.useCallback(() => {
let sum = 0;
for (let i = 0; i < 1000000000; i++) {
sum += i;
}
return sum;
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<input value={value} onChange={(event) => setValue(event.target.value)} />
<ChildMemo onClick={handleClick} />
</div>
);
}
// ChildMemo component is wrapped with React.memo to prevent unnecessary re-renders
const ChildMemo = React.memo(({ onClick }) => {
useEffect(() => {
return () => {
console.log("Child component unmounted");
}
}, []);
console.log("Child component rendered");
return <button onClick={() => console.log(onClick())}>Click me</button>;
})