React各种知识点

React各种知识点

理解 useRef、useMemo、useCallback

memo 给子组件包裹一层memo, 父组件更新不会影响子组件更新。

useRef 可用来存储一个引用值(不会受 re-render 影响),也可用来获取 dom 节点。

useMemo 用来缓存一个值。当依赖项为空数组时,缓存值永远不会变。当有依赖性时,每次 re-render 如果依赖改变,那么将重新执行函数,将新的函数返回值作为缓存的数据。

  1. 传递给子组件的引用数据类型需要使用 useMemo 包裹。其实和上面说的传递函数给组件,函数要用 useCallback 包裹起来是同一个道理。函数也是引用数据类型,是一个特殊的对象!
  2. hook 使用 useMemo 包裹,为什么 hook 要使用 useMemo 包裹?因为我们使用 hook 本质上是在调用一个函数执行的计算结果。每次 re-render 的时候,都去执行一下 hook 函数的话,可能会造成性能上的损失。所以可以使用 useMemo 将函数执行的计算结果缓存起来。
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
42
43
44
45

// 父组件

import { useCallback, useMemo, useState } from 'react'
import Foo from '@/components/foo'
import './App.css'

function App() {
const obj = useMemo(() => ({
name: 'coder',
age: 28
}), [])

const [count, setCount] = useState(0)

return (
<>
<button onClick={()=>setCount(count + 1)}>{count}</button>
<Foo obj={obj}/>
</>
)
}

export default App

// 子组件

import { memo } from "react"

interface Props {
obj: {
name: string,
age: number
}
}
const Index = (props: Props) => {
console.log('re-render');

return (
<button>按钮</button>
)
}

export default memo(Index)

useCallback 是 useMemo 的语法糖,相当于返回一个函数。传递给子组件的函数使用 useCallback 包裹, re-render 时前后创建的两个函数引用地址并不一样,Object.is() => false

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
42
43
44
45
46
47
import { useCallback, useState } from 'react'
import Foo from '@/components/foo'
import './App.css'

function App() {
const request = useCallback(() => {
setTimeout(() => {
console.log('请求');
}, 10)
}, [])

// const request = () => {
// setTimeout(() => {
// console.log('请求');
// }, 10)
// }

const [count, setCount] = useState(0)

return (
<>
<button onClick={()=>setCount(count + 1)}>{count}</button>
<Foo request={request} />
</>
)
}

export default App

// 子组件

import { memo } from "react"

interface Props {
request: () => void
}

const Index = (props: Props) => {
console.log('re-render');

return (
<button onClick={props.request}>按钮</button>
)
}

export default memo(Index)

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
interface Cb{
(...arg: unknown[]):unknown
}

const catchMap = new Map();
export default (key:string, callback:Cb) =>{
return async (cache=true) => {
const result = cacheMap.get(key)
if(cache && result) {
return result;
}
const res = await callback()
cacheMap.set(key,res)

return res
}
}

// 使用
function App () {
const [count,setCount] = useState(0);
useEffect(() => {
fn()
})

async function fn() {
const res = await getStatus()

const getStatus = useCache('getStatus', () => {
const arr = [1,2,3,4]
const res = [] as number[]
arr.forEach(num => {
console.log(num);
res.push(num)
});

return res
})

function add () {
setCount(c => c + 1)
}

return (
<>
<h2>==</h2>
<br />
{count}
<br />
<button onClick={add}>add</button>
<h2>==</h2>
</>
)
}
}

在函数式组件中使用 ref

  1. 获取当前组件内的 dom 节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { useRef, useEffect, useState } from 'react'
import Foo from '@/components/foo'
import './App.css'

function App() {
const button = useRef(null)

useEffect(() => {
console.log(button.current);
}, [])

return (
<>
<h2>==</h2>
<button ref={button} >按钮</button>
<h2>==</h2>
</>
)
}

export default App
  1. 获取子组件的dom节点

使用 forwardRef 转发。

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

// 父组件

import { useRef, useEffect } from 'react'
import Foo from '@/components/foo'
import './App.css'

function App() {
const button = useRef(null)

useEffect(() => {
console.log(button.current);
}, [])

return (
<>
<h2>==</h2>
<Foo ref={button} />
<h2>==</h2>
</>
)
}

export default App
// 子组件

import React, { forwardRef } from "react"

interface Props {
name?: string
}

const Index = (props: Props, ref: any) => {
return (
<button ref={ref}>name: {props.name}</button>
)
}

export default forwardRef(Index)
  1. memo配合forwardRef一起使用

用 memo 包裹 forwardRef 转发后的组件,防止子组件不必要的 re-render。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { forwardRef, memo } from "react"

interface Props {
name?: string
}

const Index = (props: Props, ref: any) => {
// console.log(123);

return (
<button ref={ref}>name: {props.name}</button>
)
}

export default memo(forwardRef(Index))
作者

Jimmy

发布于

2023-02-26

更新于

2023-04-28

许可协议

评论