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
的一般规则中,只有父组件的某一个状态改变,父组件下面所有的子组件不论是否使用了该状态,都会进行重新渲染
父子组件都进行了重新渲染.png
优化阶段一: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> ) }
只有父组件重新渲染了.png
在父子组件之间没有任何通信的情况下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;
子组件数据没有变化但是却重新渲染了.png
结果发现:子组件数据没有变化但是却重新渲染了!!不是用了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;
About组件没有重新渲染.png
这样就达到优化效果啦~
充满知识的一天.png