1,为什么需要Hook?
Hook是React 16.8新增的特性,他可以**让我们在不编写class的情况下使用state以及其他的React特性
**(比如生命周期)
1,1不使用hooks
import React, { PureComponent } from 'react';
class App extends PureComponent {
constructor (props) {
super(props);
this.state = {
message: 'hello World'
};
}
render () {
const { message } = this.state
return (
<div>
{message}
</div>
)
}
}
export default App
在类组件中可以保存原来的状态,但是函数式组件就无法保存自己的状态,当修改值得时候,函数组件也不会重新进行渲染。
,就算可以监听数据的改变,但是也会重新初始化值,达不到修改效果。
Hooks的出现
- 他可以让我们在不编写class的情况下使用state以及其他的react的特性
使用场景:
-
Hooks的出现基本可以代替我们之前所有使用的class的部分
-
但是如果是一个旧的项目,你并不需要直接将所有的代码重构为Hooks,因为他完全向下兼容,你可以使用渐进式来使用它(意思是新的组件就用Hooks来编写)
-
Hooks只能在函数组件中使用,不能在类组件中,或者函数组件之外的地方使用
-
只能在函数组件首部使用Hook
-
可以自定义Hook使用,但是命名必须要以use开头(useFoo)
Hooks实现计数器
import { memo, useState } from 'react'
function Counter () {
// useSate返回的是数组,将useSate进行解构
const [counter, setCounter] = useState(0)
return (
<div>
<h2>计算总数:{counter}</h2>
<button onClick={e => setCounter(counter + 1)}>+</button>
<button onClick={e => setCounter(counter + 1)}>-</button>
</div>)
}
// 保存状态
export default memo(Counter)
相信通过以上的案例,你已经感受到Hooks的便捷了,接下来,就让我们开始正式学习Hooks
useState
(钩入状态)
useEffect
一个函数组件中可以存在多个useEffect(从上到下依次执行)
useEffect(() => {
// 当前传入的回调函数会在组件被渲染完成后,自动执行
// 比如网络请求/DOM操作/事件监听
document.title = counter + ''
})
useEffect(() => {
// 当前传入的回调函数会在组件被渲染完成后,自动执行
// 比如网络请求/DOM操作/事件监听
document.title = counter + ''
return () => {
console.log('取消监听redux中数据的变化')
}
})
数据改变都会导致喊函数组件进行重新加载,这样useEffect就会多次进行不必要的调用,这就造成了性能问题
所以要实现useEffect的性能优化,要限制哪些数据改变才造成useEffect的调用
useEffect(() => {
// 当前传入的回调函数会在组件被渲染完成后,自动执行
// 比如网络请求/DOM操作/事件监听
document.title = counter + ''
// []表示只在组件首次创建的时候进行调用
}, [])
useEffect(() => {
// 当前传入的回调函数会在组件被渲染完成后,自动执行
// 比如网络请求/DOM操作/事件监听
document.title = counter + ''
// 表示受到counter的影响,counter改变导致页面重新加载会导致useEffect的调用
// []表示只在组件创建的时候调用一次
}, [counter])
useContext
方便组件之间实现数据共享
先使用createContext创建UserContext
import { createContext } from 'react'
const UserContext = createContext()
export { UserContext }
然后对Counter组件进行数据共享
import React, { memo } from 'react'
import Counter from './Counter'
import { UserContext } from './context'
function App () {
return (
<div>
<UserContext.Provider value={{ name: 'why' }}>
<Counter/>
</UserContext.Provider>
</div>
)
}
export default memo(App)
然后在Counter组件中接收数据
import { memo, useContext, useEffect, useState } from 'react'
import { UserContext } from './context'
function Counter () {
// 使用useContext进行数据的获取
const useValue = useContext(UserContext)
// useSate返回的是数组,将useSate进行解构
const [counter, setCounter] = useState(0)
useEffect(() => {
// 当前传入的回调函数会在组件被渲染完成后,自动执行
// 比如网络请求/DOM操作/事件监听
document.title = counter + ''
// 表示受到counter的影响,counter改变导致页面重新加载会导致useEffect的调用
}, [counter])
console.log(useValue)
return (
<div>
<h2>计算总数:{counter}</h2>
<button onClick={e => setCounter(counter + 1)}>+</button>
<button onClick={e => setCounter(counter + 1)}>-</button>
</div>)
}
// 保存状态
export default memo(Counter)
useReducer
useCallback
useCallback通常是用来进行性能优化的
当我们需要将一个函数传递给子组件的时候,最好使用useCallback传递给子组件时,将优化后的函数传递给子组件(
当父组件重新渲染时,如果没有使用 useCallback
对函数进行优化,每次渲染都会创建一个新的函数实例,即使函数的内容没有变化。这可能会导致子组件的不必要的重新渲染。)
2.
注意useCallback会出现闭包陷阱
import React, { memo } from 'react'
import Counter from './Counter'
import { UserContext } from './context'
function App () {
function getName () {
console.log(123123)
}
return (
<div>
<UserContext.Provider value={{ name: 'why' }}>
<Counter/>
</UserContext.Provider>
</div>
)
}
export default memo(App)
在这段函数中,数据每发生改变。函数就会被重新创建一次,虽然函数不会被调用,但是每次都会创建一个新的函数,这就会造成性能上的影响
使用useCallback进行优化
import React, { memo, useCallback } from 'react'
import Counter from './Counter'
import { UserContext } from './context'
// useCallback可以限制某个数据改变,getName函数才进行重新加载
function App () {
const getName = useCallback(function () {
console.log(123123)
}, [])
return (
<div>
<UserContext.Provider value={{ name: 'why' }}>
<Counter/>
</UserContext.Provider>
</div>
)
}
export default memo(App)
useMemo
useMemo的作用和useCallback类似,但是useMemo是传入返回值,useCallback是传入函数
import { memo, useMemo } from 'react'
function Sum () {
let sum = 0
let getSum = (number) => {
for (let i = 0; i < number; i++) {
sum = i + sum
}
return sum
}
// useMemo返回的是函数的返回值(sum的值是函数的返回值)
let Sum = useMemo(() => {
return getSum(100)
}, [])
return (
<div>
<h2>计算结果:{Sum}</h2>
</div>
)
}
export default memo(Sum)
// 当组件重新渲染的时候,不想让memoizedValue重新创建
const memoizedValue = useMemo(() => ({
name: 'topu',
age:20
}),[])
useRef
用于获取组件实列
例如在Sum.jsx组件中
import { memo, useMemo, useRef } from 'react'
function Sum () {
const sumRef = useRef()
let sum = 0
let getSum = (number) => {
for (let i = 0; i < number; i++) {
sum = i + sum
}
return sum
}
let Sum = useMemo(() => {
return getSum(100)
}, [])
let getRef=function (){
console.log(sumRef.current)
}
return (
<div>
<h2 ref={sumRef} onClick={getRef}>计算结果:{Sum}</h2>
</div>
)
}
export default memo(Sum)
useLayoutEffect
useLayoutEffect是在渲染内容更新到DOM之前执行,会阻塞DOM的更新,但是useEffect则相反,他不会阻碍DOM更新
官方建议的是使用useEffect,但是在有些特殊场景的时候还是会使用useLayoutEffect