react_hooks的useCallback,高阶函数memo
一、概念和作用
1、memo高阶函数:
memo解决的是函数式组件的无效渲染问题,当函数式组件重新渲染时,会先判断数据是否发生了变化。相当于类组件的PureComponent(默认提供ShouldComponentUpdate)
2、useCallback:
1)、useCallback会返回一个函数的memoized(记忆的)值
2)、在依赖不变的情况下,多次定义(如:函数)的时候,返回的值是相同的 。
3)、格式:let 新的函数 = useCallback(曾经的函数, [依赖的值])
二、使用场景:
1、memo高阶函数的使用场景:
不论父组件是什么类型的组件,子组件是否渲染 :
1)、 如果子组件是类组件(继承自PureComponent)
那么是否渲染由props和state是否改变决定;
2)、如果子组件是函数式组件
只要父组件渲染,子组件会无条件渲染。如下是代码示例:
//父组件: import { useState } from "react"; import SonFn from "./SonFn"; export default () => { console.log("父组件"); const [count, setCount] = useState(1); let changeCount = () => { setCount(count + 1); } return ( <> <h1>useCallback</h1> <p>{count}</p> <input type="button" value="修改count" onClick={changeCount} /> <hr /> <SonFn /> </> ) } //子组件: ./SonFn.js export default ()=>{ console.log("子组件"); return ( <div> <h5>子组件(函数式组件)</h5> </div> ) }
只要点击按钮"修改count”,父组件就会刷新,而子组件SonFn也会无条件渲染(这是无效的渲染)。
3)、解决方案:
把子组件用高阶函数memo进行包裹,就能解决子组件的无条件渲染问题,即:子组件的渲染就会由props和state决定,有点像类组件继承自PureComponent的感觉。
如下是代码(只需要把子组件的代码进行修改就行):
//子组件: import React,{memo} from 'react' const SonFn = ()=>{ console.log("子组件"); return ( <div> <h5>子组件(函数式组件)</h5> </div> ) } export default memo(SonFn);
2、useCallback的使用场景:
父组件是函数式组件,子组件也是函数式组件(并且用memo包裹)
1)、子组件的属性是数据:
如果数据不变化,那么子组件不渲染,如果数据发生变化,那么子组件渲染。这里就没有性能问题。
//父组件 import { useState,useCallback } from "react"; import SonFn from "./SonFn"; export default () => { console.log("父组件UseCallback"); const [count, setCount] = useState(1); let changeCount = () => { setCount(count + 1); } return ( <> <h1>useCallback1</h1> <p>{count}</p> <input type="button" value="修改count" onClick={changeCount} /> <hr/> {/*此处给子组件传入了数据count,count只要发生变化,子组件就会重新渲染*/} <SonFn count={count} /> </> ) } //子组件: ./SonFn.js import React,{memo} from 'react' const SonFn = ({count})=>{ console.log("子组件"); return ( <div> <h5>子组件(函数式组件)</h5> <p>{count}</p> </div> ) } export default memo(SonFn);
2)、子组件的属性是函数时,就会出现问题:
父组件刷新了,子组件依然会刷新。因为,父组件(函数式)每次刷新时,函数都会重新定义,那么传给子组件的函数属性必然会发生变化。所以子组件会刷新,如下是示例代码:
//父组件: import { useState } from "react"; import SonFn from "./SonFn"; export default () => { console.log("父组件"); const [count, setCount] = useState(1); let changeCount = () => { setCount(count + 1); } let increment = ()=>{ console.log("increment"); } return ( <> <h1>useCallback</h1> <p>{count}</p> <input type="button" value="修改count" onClick={changeCount} /> <hr/> <SonFn onMyClick={increment} /> </> ) } //子组件: ./SonFn.js import React,{memo} from 'react' const SonFn = ()=>{ console.log("子组件"); return ( <div> <h5>子组件(函数式组件)</h5> </div> ) } export default memo(SonFn);
3)、解决方案:把传给子组件的函数属性,用useCallback包裹。
格式:let 新的函数 = useCallback(曾经的函数, [依赖的值])
如下是修改后的代码(只需要修改父组件的代码):
以下代码把increment函数进行了包裹
export default () => { console.log("父组件"); const [count, setCount] = useState(1); let changeCount = () => { setCount(count + 1); } let increment = useCallback(()=>{ console.log("increment"); },[]) // 该函数永远不会重新定义(没有依赖) /* let increment = useCallback(()=>{ console.log("increment"); },[count]) // 当count的值发生变化是,该函数才会重新定义 */ return ( <> <h1>useCallback</h1> <p>{count}</p> <input type="button" value="修改count" onClick={changeCount} /> <hr/> <SonFn onMyClick={increment} /> </> ) }
三、总结:
1、“万恶之源” :函数式组件每次重新渲染时,都会把函数体里的所有代码执行一遍。
2、useCallback解决的是 防止函数式组件里的 子函数(闭包) 多次被定义。既就是:useCallback是保证函数式组件重新渲染时,组件里的函数(闭包)只被定义一次!
标签:count,函数,hooks,memo,SonFn,useCallback,组件 From: https://www.cnblogs.com/limou956259/p/17898001.html