了解 React 之 hooks(二)
前言
在前面的文章 了解 React 之 hooks(一) 中介绍了 hooks 的 4 个基础 API(useState、useEffect、useContext、useReducer)。本文主要介绍 hooks 的 3 个性能优化 API,分别为 React.memo、React.useCallback、React.Memo。
React.memo
React.memo 的作用类似于类组件的 shouldComponentUpdate 和 PureComponent,在子组件 props 未更新的时候不渲染子组件,值得注意的是 React.memo 也只能做浅层比较,对于复杂对象只能比较对象的引用。但是可以通过在 React.memo 中传递比较函数来做复杂类型的更新判断。
1. 使用 React.memo 前
App 组件:
import React,{useState} from 'react';
import ReactDOM from 'react-dom';
import Child from './Child';
function App() {
const [count,setCount]=useState(0);
return <div onClick={setCount(count+1)}>
{count}
<Child name="子组件"/>
</div>
}
export default App
Child 组件:
import React from 'react';
function Child(props) {
console.log(props);
return <div>
{props.name}
</div>
}
export default Child
以上案例中当更新 count 时,Child 组件的 props 并未发生变化。如果不做任何处理,每次点击事件执行时,Child 都会重新渲染。这时候就需要 React.memo 来优化。
2. 使用 React.memo 后
import React,{useState} from 'react';
import ReactDOM from 'react-dom';
import Child from './Child';
function App() {
const [count,setCount]=useState(0);
return <div onClick={setCount(count+1)}>
{count}
<Child name="子组件"/>
</div>
}
export default App
Child 组件:
import React from 'react';
function Child(props) {
console.log(props);
return <div>
{props.name}
</div>
}
export default React.memo(Child)
3. React.memo 高级用法
前面说到 React.memo 只是浅层比较,如果要进行深度比较,则需要传入比较函数。
function MyComponent(props) {
/* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
}
export default React.memo(MyComponent, areEqual);
React.useCallback
在以上案例的基础上,给 App 父组件上添加一个事件,传递给 Child 子组件。
1. 使用 React.useCallback 前
import React,{useState} from 'react';
import ReactDOM from 'react-dom';
import Child from './Child';
function App() {
const [count,setCount]=useState(0);
const [title,setTitle]=useState("标题");
const callback = ()=>{
setTitle("更改标题")
}
return <div onClick={setCount(count+1)}>
{count}
<Child name="子组件" callback={callback}/>
</div>
}
export default App
Child 组件:
import React from 'react';
function Child(props) {
console.log(props);
return <div>
{props.name}
</div>
}
export default React.memo(Child)
可以发现在执行点击事件的时候,Child 子组件依然每次都渲染了。这就很奇怪了,为啥 props 没有改变,使用了 React.memo。但是还是会重复渲染。
其实在函数组件渲染的时候,函数会重新执行。那么每次创建的 callback 函数也不相同。所以才会导致重新渲染。这时候就需要用到 useCallback。其原理是将函数缓存起来,因此每次创建的就是同一函数,所以不会重复渲染。
2. 使用 React.useCallback 后
import React,{useState,useCallback} from 'react';
import ReactDOM from 'react-dom';
import Child from './Child';
function App() {
const [count,setCount]=useState(0);
const [title,setTitle]=useState("标题");
const callback = ()=>{
setTitle("更改标题")
}
// 通过 useCallback 缓存 callback,并将缓存的 callback 传递给 Child
const memoizedCallback = useCallback(callback, [])
return <div onClick={setCount(count+1)}>
{count}
<Child name="子组件" callback={memoizedCallback}/>
</div>
}
export default App
Child 组件:
import React from 'react';
function Child(props) {
console.log(props);
return <div>
{props.name}
</div>
}
export default React.memo(Child)
3. React.useCallback 高级用法
当函数的参数发生变化时,可以添加在 useCallback 中第二个参数数组中,这样就可以实现每次参数变化时 useCallback 就会重新创建,组件会重新渲染。其特性类似于 useEffect。
React.Memo
前面的两个 API React.memo 和 React.useCallback 主要是为了减少重复渲染。而 React.Memo 则是为了减少重复计算。
1. 使用 React.Memo 前
import React,{useState} from 'react';
import ReactDOM from 'react-dom';
import Child from './Child';
function App() {
const [count,setCount]=useState(0);
const getResult = ()=>{
let result = 0;
for(let i = 0;i<100000;i++){
result+=i;
}
return result;
}
return <div onClick={setCount(count+getResult())}>
点此增加{count}
<Child name="子组件"/>
</div>
}
export default App
以上案例中每次点击时 getResult 函数都会重复执行。由于 getResult 是一个计算量很大的函数,所以会影响性能。这时候就可以使用 useMemo 做计算结果缓存。
2. 使用 React.Memo 后
import React,{useState} from 'react';
import ReactDOM from 'react-dom';
import Child from './Child';
function App() {
const [count,setCount]=useState(0);
const getResult = ()=>{
let result = 0;
for(let i = 0;i<100000;i++){
result+=i;
}
return result;
}
const base = useMemo(getResult, []);
return <div onClick={setCount(count+base)}>
点此增加{count}
<Child name="子组件"/>
</div>
}
export default App
3. React.Memo 高级用法
同样的,当计算函数的参数变化时,也可以添加到 useMemo 的第二个参数数组中,就可能实现当参数变化时进行更新。