React笔记(十四)
前言
eg:代表代码对照 若文章有误,欢迎读者留言反馈useCallback【记忆函数】
【记忆函数,实现了类似记忆组件、计算属性的功能,也能进行scu优化】
useCallback
记忆函数,效果类似记忆组件memoize-one
,把某个函数进行缓存,然后把当前函数体返回给你
- 正常情况下,父组件中当前数据改变,当前父组件无条件刷新,当然子组件也随之刷新,当前子组件依赖的数组没有发生改变,咱么就可以通过scu技术,不让他刷新
parent.jsx父组件
1 | // 引入useState、useCallback两个hooks |
Child.jsx子组件
1 | // 引入memo高阶组件【浅比较】 |
- 如果给子组件传递个函数进去,就算是依赖的数据和函数没有变化,scu也会被打破,子组件被动随着这个函数刷新[尽管它没执行]
问题:每次组件刷新,组件里面的执行函数都会从新创建,所以刷新前后不是同一函数
分析问题:这里多说一句,一般把函数式组件理解为class组件render函数的语法糖,所以每次重新渲染的时候,函数式组件内部所有的代码都会重新执行一遍。所以上述代码中每次render,handleClick都会是一个新的引用,所以也就是说传递给SomeComponent组件的props.onClick一直在变(因为每次都是一个新的引用),所以才会说这种情况下,函数组件在每次渲染的时候如果有传递函数的话都会重渲染子组件。
解决方法:有了
useCallback
就不一样了,你可以通过useCallback
获得一个记忆后的函数,那么改变父组件中的num就不会影响子组件刷新,因为函数也做了缓存,注意子组件也要使用memo高阶组件
parent.jsx父组件
1 | // 引入useState、useCallback两个hooks |
Child.jsx子组件
1 | // 引入memo高阶组件【浅比较】 |
userMemo【记忆组件,不需要scu】
useCallback
的功能完全可以由useMemo
所取代,如果你想通过使用useMemo
返回一个记忆函数也是完全可以的
所以前面使用useCallback
的例子可以使用useMemo
进行改写:
1 | // 使用useMemo对某个数据进行缓存,强调的是结果值 |
parent.jsx父组件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41// 引入useState、useCallback、useMemo三个hooks
import React, { useState, useCallback, useMemo } from 'react'
import Child from './Child'
// useCallback,记忆函数,效果类似记忆组件,memoize-one,把某个函数进行缓存,然后把当前函数体返回给你。
// 作用:如果子组件传入的有数据和函数,scu会被打破。可以对函数进行useCallback缓存,这样scu就又生效了
// 函数组件内容可以看成类组件render的语法糖。
const Parent = () => {
const [num, setNum] = useState(0)
const [arr, setArr] = useState([1, 2, 3, 4])
// const handleAdd = () => {
// setArr([...arr, 5])
// }
// 使用useCallback进行scu优化[缓存了arr,如果arr发生改变就会重新刷新子组件]
// const handleAdd = useCallback(() => {
// setArr([...arr, 5])
// }, [arr]) //第二参数有依赖性 变量改变触发第一个函数
// 使用useMemo来实现useCallback的功能
const handleAdd = () => {
setArr([...arr, 5])
}
const child = useMemo(() => <Child arr={arr} handleAdd={handleAdd} />, [arr])
return (
<div>
<h3>Parent</h3>
<p>{num}</p>
{ /* 正常情况下,父组件中当前数据改变,当前父组件无条件刷新,当然子组件也随之刷新 */}
<button onClick={() => setNum(num + 1)}>点击+1</button>
{/* 当前数据改变,当前组件无条件刷新,当然子组件也随之刷新 */}
{/* 当前子组件依赖的数组没有发生改变,咱么就可以通过scu技术,不让他刷新 */}
{/* 如果给子组件传递个函数进去,就算是依赖的数据和函数没有变化,scu也会被打破,子组件被动随着这个函数刷新[尽管它没执行] */}
{/* 问题:每次组件刷新,组件里面的执行函数都会从新创建,所以刷新前后不是同一函数 */}
{/* <Child arr={arr} handleAdd={handleAdd} /> */}
{/* 使用useMemo来实现useCallback的功能,落脚点传递的是值,同时也不需要scu */}
{child}
</div>
)
}
export default Parent
Child.jsx子组件
1 | import React from 'react' |
唯一的区别是:useCallback 不会执行第一个参数函数,而是将它返回给你,而 useMemo 会执行第一个函数并且将函数执行结果返回给你。所以在前面的例子中,可以返回 handleClick 来达到存储函数的目的。
所以 useCallback 常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。而 useMemo 更适合经过函数计算得到一个确定的值,比如记忆组件。
从例子可以看出来,useMemo只有在第二个参数数组的值发生变化时,才会触发子组件的更新。
useRef【保存引用值】
- 受控组件
1 | // 引入useRef、useState两个hooks |
- 非受控组件ref
1 | // 引入useRef、useState两个hooks |
UseImperativeHandleDemo【透传,了解即可】
通过 useImperativeHandle 用于让父组件获取子组件内的索引,这种方式,App 组件可以获得子组件的 input 的 DOM 节点。
1 | // 引入useRef、useEffect、useImperativeHandle、forwardRef四个hooks |
自定义hooks
自定义hooks是在react-hooks
基础上的一个拓展,可以根据业务需要制定满足业务需要的hooks,更注重的是逻辑单元。通过业务场景不同,我们到底需要react-hooks
做什么,怎么样把一段逻辑封装起来,做到复用,这是自定义hooks产生的初衷。
我们设计的自定义react-hooks
应该是长的这样的。
1 | const [ xxx , ... ] = useXXX(参数A,参数B...) |
在我们在编写自定义hooks的时候,要关注的是传进去什么,返回什么。返回的东西是我们真正需要的。更像一个工厂,把原材料加工,最后返回我们。
1 | import React, { useState, useMemo } from 'react' |