1. 什么是useCallback Hook?
useCallback
用于优化代码
, 可以让对应的函数只有在依赖发生变化时才重新定义
怎么理解呢?先看一个小demo——在子组件中定义两个变量和两个方法,在子组件中进行数据操作
import React, {useState} from 'react';
function Home(props) {
console.log('Home被渲染了');
return (
<div>
<p>Home</p>
</div>
)
}
function About(props) {
console.log('About被渲染了');
return (
<div>
<p>About</p>
</div>
)
}
function App() {
console.log('App被渲染了');
const [numState, setNumState] = useState(0);
const [countState, setCountState] = useState(0);
function increment() {
setNumState(numState + 1);
}
function decrement() {
setCountState(countState - 1);
}
return (
<div>
<p>numState = {numState}</p>
<p>countState = {countState}</p>
<Home/>
<About/>
</div>
)
}
export default App;
结果发现不管是点击Home/About
里面的按钮,虽然Home
和About
里头的并没有numState
和countState
,也就是Home
和About
组件里头的数据并没有发生变化但是还是被重新渲染了,特别消耗性能
结论:
在react
的一般规则中,只有父组件的某一个状态改变,父组件下面所有的子组件不论是否使用了该状态,都会进行重新渲染
优化阶段一:memo函数
对于没有用到被改变的那个状态的组件来说,理想情况下无需重新渲染。React.memo就是解决这个问题的一个函数
用法:
memo函数接收一个组件式函数,返回一个新的函数
(顺便说一句,这个是什么?嘿嘿,我想起来了,是高阶组件,所以memo
函数实际就是各个高阶组件,哈哈,知识被串起来的感觉真好~
用法:
const MemoHome = memo(Home);
const MemoAbout = memo(About);
function App() {
console.log('App被渲染了');
const [numState, setNumState] = useState(0);
const [countState, setCountState] = useState(0);
function increment() {
setNumState(numState + 1);
}
function decrement() {
setCountState(countState - 1);
}
return (
<div>
<p>numState = {numState}</p>
<p>countState = {countState}</p>
<MemoHome/>
<MemoAbout/>
</div>
)
}
在父子组件之间没有任何通信的情况下Home和About组件确实不会重新渲染
使用mome但是父子组件之间存在通讯
import React, {useState, memo} from 'react';
function Home(props) {
console.log('Home被渲染了');
return (
<div>
<p>Home</p>
<button onClick={()=>{props.handler()}}>增加</button>
</div>
)
}
function About(props) {
console.log('About被渲染了');
return (
<div>
<p>About</p>
<button onClick={()=>{props.handler()}}>减少</button>
</div>
)
}
// 使用memo函数
const MemoHome = memo(Home);
const MemoAbout = memo(About);
function App() {
console.log('App被渲染了');
const [numState, setNumState] = useState(0);
const [countState, setCountState] = useState(0);
function increment() {
setNumState(numState + 1);
}
function decrement() {
setCountState(countState - 1);
}
return (
<div>
<p>numState = {numState}</p>
<p>countState = {countState}</p>
{/*<button onClick={()=>{increment()}}>增加</button>*/}
{/*<button onClick={()=>{decrement()}}>减少</button>*/}
// 将操作数据的方法传递过去但是组件内容不含父组件数据
<MemoHome handler={increment}/>
<MemoAbout handler={decrement}/>
</div>
)
}
export default App;
结果发现:子组件数据没有变化但是却重新渲染了!!不是用了memo
函数么?怎么回事?
当前Home和About重新渲染的原因是因为父组件中的数据发生了变化, 会重新渲染父组件重新渲染父组件, 就会重新执行父组件函数重新执行父组件函数, 就会重新定义increment/decrement
既然increment/decrement
是重新定义的, 所以就和上一次的不是同一个函数了,既然不是同一个函数, 所以Home和About接收到的内容也和上一次的不一样了,既然接收到的内容和上一次不一样了, 所以就会重新渲染
怎么解决这个问题呢?useCallback
诞生啦~
useCallback
的用法:
- 接收两个参数:
- 第一个参数:是一个组件,接收一个需要防止做无用功重新渲染的子组件
- 第二个参数:是一个数组[],存放对应的函数依赖的数据
//核心代码
// 以下代码的作用: 只要countState没有发生变化, 那么useCallback返回的永远都是同一个函数
const decrement = useCallback(()=>{
setCountState(countState - 1);
}, [countState]);
// 完整代码
import React, {useState, memo, useCallback} from 'react';
function Home(props) {
console.log('Home被渲染了');
return (
<div>
<p>Home</p>
<button onClick={()=>{props.handler()}}>增加</button>
</div>
)
}
function About(props) {
console.log('About被渲染了');
return (
<div>
<p>About</p>
<button onClick={()=>{props.handler()}}>减少</button>
</div>
)
}
const MemoHome = memo(Home);
const MemoAbout = memo(About);
function App() {
console.log('App被渲染了');
const [numState, setNumState] = useState(0);
const [countState, setCountState] = useState(0);
function increment() {
setNumState(numState + 1);
}
function decrement() {
setCountState(countState - 1);
}
// 以下代码的作用: 只要countState没有发生变化, 那么useCallback返回的永远都是同一个函数
const decrement = useCallback(()=>{
setCountState(countState - 1);
}, [countState]);
return (
<div>
<p>numState = {numState}</p>
<p>countState = {countState}</p>
<MemoHome handler={increment}/>
<MemoAbout handler={decrement}/>
</div>
)
}
export default App;
这样就达到优化效果啦~