hooks更多用法
# Hooks 更多用法
# useRef ()
使用场景:在 React
中进行 DOM 操作时,用来获取 DOM
作用:返回一个带有 current
属性的可变对象,通过该对象进行 DOM 操作了
const inputRef = useRef(null)
使用
const App = () => {
const inputRef = useRef(null)
const add = () => {
console.log(inputRef.current.value)
}
return (
<section className="todoapp">
<input type="text" placeholder="请输入内容" ref={inputRef} />{' '}
<button onClick={add}>添加</button>
</section>
)
}
2
3
4
5
6
7
8
9
10
11
12
注意:useRef
不仅仅可以用于操作 DOM , 还可以操作组件
# useContext ()
使用场景:跨组件共享通信
Context
作用:实现跨组件传递数据,而不必在每个级别手动传递 props
,简化组件之间的数据传递过程
const Context = createContext(defaultValue)
const value = useContext(conText)
2
Context
对象包含了两个组件
<Context.Provider value>
通过value
属性提供数据<Context.Consumer>
通过render-props
模式,在JSX
中获取Context
提供的数据
在函数组件中,获取 Context
中的值,需需要配合 Context
一起使用而 useContext
和 Context.Consumer
的区别在于:获取数据的位置不同
示例
import React, { useState, useContext } from 'react'
const Context = React.createContext()
const App = () => {
const [color, setColor] = useState('red')
return (
<Context.Provider value={color}>
<div>
<h1>我是根组件</h1>
<div>颜色:{color}</div>
<button onClick={() => setColor('yellow')}>修改</button>
<Father></Father>
</div>
</Context.Provider>
)
}
const Father = () => {
return (
<div>
<h3>我是父组件</h3>
<Child></Child>
</div>
)
}
const Child = () => {
const color = useContext(Context)
return (
<div>
<h5>我是子组件--{color}</h5>
</div>
)
}
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
# React-memo ()
使用场景:在父组件的状态更新的时候,子组件就会无条件的一起更新
渲染过程
- 子组件
props
变化时更新过程:组件代码执行 -> JSX Diff(配合虚拟 DOM) -> 渲染(变化后的内容) - 子组件无变化更新过程:代码组件执行 -> JSX Diff (配合虚拟DOM)
为了提升性能,优化不必要的无变化子组件更新,这种情况就可以使用 React.memo
高阶组件
import { useState } from 'react'
import ReactDOM from 'react-dom'
const Child2 = ({ count }) => {
console.log('Child2 子组件代码执行了')
return <div style={{ backgroundColor: '#abc' }}>子组件2:{count}</div>
}
const Child1 = () => {
console.log('Child1 子组件代码执行了')
return <div style={{ backgroundColor: '#def' }}>子组件1</div>
}
const App = () => {
const [count, setCount] = useState(0)
return (
<div style={{ backgroundColor: 'pink', padding: 10 }}>
<h1>计数器:{count}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
<hr />
{/* 子组件 */}
<Child1 />
<br />
<Child2 count={count} />
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
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
此示例,在子组件2更新时, 子组件1也会执行更新
memo
作用:记忆上一次的更新渲染结果,在 props
没有变化时复用该结果,避免函数不必要的更新
使用示例
import { useState, memo } from 'react' //导入
const Child2 = memo(({ count }) => {
console.log('Child2 子组件代码执行了')
return <div style={{ backgroundColor: '#abc' }}>子组件2:{count}</div>
})
const Child1 = memo(() => {
console.log('Child1 子组件代码执行了')
return <div style={{ backgroundColor: '#def' }}>子组件1</div>
})
2
3
4
5
6
7
8
9
10
- 参数:需要被记忆的组件,不必要更新的组件
- 返回值:
React
记住的Child
组件
原理:通过检查对比更新前后 props
是否相同,来决定是否复用上一次的渲染结果
注意: 不是所有的组件都适用 memo
,需要经常更新渲染的组件使用 memo
性能反而会降低
# 浅层对比
默认情况下,React.memo
只会对更新前后的 props
进行浅对比
也就是说,对于对象类型的 prop
来说,只会比较引用(地址)
import { useState, memo } from 'react'
import ReactDOM from 'react-dom'
const Child2 = memo(({ count }) => {
console.log('Child2 子组件代码执行了')
return <div style={{ backgroundColor: '#abc' }}>子组件2:{count}</div>
})
const Child1 = memo(({ obj }) => {
console.log('Child1 子组件代码执行了')
return (
<div style={{ backgroundColor: '#def' }}>
子组件1 id:{obj.id} name:{obj.name} age:{obj.age}
</div>
)
})
const App = () => {
const [count, setCount] = useState(0)
const obj = {
name: 'yd',
id: 11,
age: 22,
}
return (
<div style={{ backgroundColor: 'pink', padding: 10 }}>
<h1>计数器:{count}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
<hr />
{/* 子组件 */}
<Child1 obj={obj} />
<br />
<Child2 count={count} />
</div>
)
}
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
此处传入的参数为 引用数据类型
,所以在会浅层的对比地址是否发生变化所以在每次更新组件时,创建的 obj 的地址是不同的所以在更新 DOM 也会更新此组件
可以使用 React.memo
的第二个参数手动控制比较
React.memo(Child,function areEqual(prevProps,nextProps){
return preProps === nextProps
})
2
3
如果返回为 true
表示记住不渲染组件,如果返回 false
表示渲染该组件
更好的解决方法
useCallback
Hook:记住函数的引用,在组件每次更新时返回相同引用的函数。useMemo
Hook:记住任意数据(数值、对象、函数等),在组件每次更新时返回相同引用的数据【功能之一】
# useCallback ()
使用场景:在使用 React.memo
时,为了组件每次更新时都能获取到相同引用的函数,就要使用到
作用:记忆传入的回调函数,这个被记住的回调函数会一直生效,知道依赖项发生改变
const memorizedCallback = useCallback(()=>{
doSomething(a,b)
},[a,b])
2
3
- 第一个参数:需要记忆的回调函数
- 第二个参数:依赖项数组,用于指定回调函数中依赖到的数据,即使没有依赖项也得传入空数组此时的回调函数会一直生效
- 返回值:
useCallback
记住的回调函数
示例
import { useState, memo, useCallback } from 'react'
import ReactDOM from 'react-dom'
const App = () => {
const [count, setCount] = useState(0)
const [money, setMoney] = useState(1000)
const help = useCallback(() => {
setCount(count - 1)
}, [count])
return (
<div>
<h1>计数器</h1>
<div>豆豆被打了{count}次</div>
<div>金钱:{money}</div>
<button onClick={() => setCount(count + 1)}>打豆豆</button>
<button onClick={() => setMoney(money + 100)}>加钱</button>
<hr />
{count < 5 ? <DouDou count={count} help={help}></DouDou> : '豆豆被打死了'}
</div>
)
}
const DouDou = memo(({ count, help }) => {
console.log('豆豆组件渲染')
return (
<div>
<h3>我是豆豆组件{count}</h3>
<button onClick={help}>续命</button>
</div>
)
})
ReactDOM.render(<App />, document.getElementById('root'))
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
# useMemo ()
使用场景:类似于 useCallback
,可以在组件更新期间保持任意数据引用相等,一般用来处理对象类型的数据
作用:记忆任意数据,这个被记住的数据会一直生效,知道依赖项发生改变
const memorizedValue = useMemo(()=>Valus,deps)
- 第一个参数:回调函数,注意该函数会被调用,并通过返回值指定需要被记住的数据
- 第二个参数:依赖项数组,用于指定回调函数中依赖到的数据
- 返回值:
useMemo
记住的数据
如何选择使用哪一个?
- 如果处理的是函数,推荐使用 useCallback Hook。
- 如果处理的是其他数据(比如,对象),推荐使用 useMemo Hook。
模拟useCallback
useCallback(fn, deps)
相当于useMemo(() => fn, deps)
。
const help = useCallback(() => {
setCount(count - 1)
}, [count])
const help = useMemo(() => {
return () => {
setCount(count - 1)
}
}, [count])
2
3
4
5
6
7
8
9
10
11
示例
import React, { useState, memo, useMemo } from 'react'
const App = () => {
const [count, setCount] = useState(5)
const [money, setMoney] = useState(1000)
// 记忆的函数=useCallback(函数, [依赖])
// 只要依赖项不变,这个函数就不会变化,如果依赖项变了,这个函数就会变化
const help = useMemo(() => {
return () => {
setCount(count + 1)
}
}, [count])
return (
<div>
<h1>根组件--{count}</h1>
<div>金钱:{money}</div>
<button onClick={() => setCount(count - 1)}>打豆豆</button>
<button onClick={() => setMoney(money + 100)}>挣钱</button>
<hr />
{count > 0 ? <DouDou help={help} count={count}></DouDou> : '豆豆被打死了'}
</div>
)
}
const DouDou = memo(({ count, help }) => {
console.log('豆豆更新了')
return (
<div>
<h3>豆豆组件被打了{count}</h3>
<button onClick={help}>续命</button>
</div>
)
})
export default App
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
# 避免昂贵的计算(计算属性)
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);