React在2018年年末推出的Hooks新特性使得之前的function无状态组件变得有状态了,让函数组件也拥有了类组件的特性。同时hooks的出现也解决了类组件中组件逻辑散布于各个生命周期中,造成组件臃肿,可复用性不强的特点。同时,对于一些组件的副作用在Hooks中也得到了较好的解决。总之,Hooks的出现,对于React组件化的开发而言将会是一次巨大的革新
这篇博客主要介绍的是关于Hooks的一些常用的钩子,大致如下:
useState,
useEffect,
useReducer,
useLayoutEffect,
useContext ,
useRef,
memo,
useMemo,
useCallback
1.useState钩子是让function组件拥有了自己的state状态,用法实例:
//react中引入useState
import React, { useState } from 'react'
function Todo () {
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => setCount(count => count + 1)}>{count}<button>
</>
)
}
useState钩子接收一个参数为state的初始值,得到一个数组,数组的第一个元素是state,第二个参数是一个方法,用语改变或者设置state的值。此时我们调用这里的setCount方法时,它里面的count参数表示的是当前我们的count值,所以,此时我们每次点击按钮时都会让count进行加一。
- useEffect && useLayoutEffect 可以让我们在我们的function中进行一些副作用的操作。
import React, {
useState,
useEffect,
useLayoutEffect
} from 'react'
function Todo () {
const [count, setCount] = useState(0)
useEffect(() => {
console.log('effect invoke')
console.log(inputRef)
return () => console.log('effect deteched ')
},[count])
useLayoutEffect(() => {
console.log('LayoutEffect invoke')
return () => console.log('LayoutEffect deteched ')
},[count])
return (
<>
<button onClick={() => setCount(count => count + 1)}>{count}<button>
</>
)
}
这里我们使用useEffect和useLayoutEffect打印几段字符串,此时我们会发现useLayoutEffect()总是先于useEffect执行,并且这两个钩子中的return返回函数中的内容也是要先于函数体中的内容先去执行。所以我们可以在useEffect中去进行一些的副作用逻辑处理,它接收两个参数,第一个参数是一个函数,表示我们这个effect需要执行的回调,第二个参数是一个数组,表示useEffect执行的依赖,及当我们数组中的值发生变化时这个effect回去执行,如果我们需要让这个effect在每次组件挂载的时候都去执行一次,那么我们也可以给它传递一个空数组。
那么关于useEffect 和 useLayoutEffect 的区别,上面我们已经说到一点,就是useLayoutEffect总是会先于useEffect去执行,其次,useLayoutEffect 还是在Html数据计算渲染前先去执行,而useEffect则是在html渲染完后去执行,所以,不推荐在useLayoutEffect中去执行太多的操作,因为由于执行回调的时间太长,会导致我们的页面出现白屏卡顿的现象,造成页面的渲染不够流畅。
3. useReducer有点类似于redux的感觉
import React, {
useReducer
} from 'react'
//该函数类似于redux中的reducer
function countReducer(state, action){
switch (action.type){
case 'add' :
return state + 1
case 'minus' :
return state - 1
default :
return state
}
}
function Todo () {
const [count, dispatchCount] = useReducer(countReducer, 0)
return (
<>
<button onClick={() => dispatchCount({type: 'add'})}>{count}<button>
</>
)
}
这里的useReducer接收两个参数,第一个是我们进行reducer的处理函数,第二个是我们的state的初始值,同样也能够得到一个数组,数组的第一个参数依然是当前的state的值,第二个参数就相当于是redux的dispatch方法,我们可以通过这个方法去发送action对到reducer中对state进行一些操作,然后拿到的是操作后的state.
4. useContext的作用其实和我们之前一篇博客中的Context是一样的,就有点类似于全局变量的样子,通过父组件去提供的stater让它所有的子组件都可以拿到:
//这是父组件中的代码段
import React from 'react'
import Todo from './Todo.js'
const MyContext = React.createContext('')
class App extends React.Component{
render() {
return (
<>
<MyContext.Provider value="test">
<Todo />
</MyContext.Provider>
</>
)
}
}
export default { App, MyContext }
import React, {
useContext
}from 'react'
//引入父组件创建的Context
import { MyContext } from './App.js'
function Todo (){
const context = useContext(MyContext)
return (
<>
<p>{context}</p>
</>
)
}
通过这样引入useContext便可以拿到父组件中context传递的值value,此时我们的p标签中显示的便是value中传递的"test"
5. useRef其实就是类似于我们之前在类组件中通过ref的方式去获取底层的组件.
import React, {
useRef,
useEffect
} from 'react'
function Todo () {
const inputRef = useRef()
useEffect(() => {
console.log(inputRef)
return null
},[])
return (
<>
<input ref={inputRef} />
</>
)
}
此时我们通过console出的inputRef就是我们得到的这个input标签,及我们只需要将我们使用useRef定义的这个inputRef传递给标签上的ref属性即可.除此之外外,useRef还可以有其他的用法,以为它里面的值总是最新的,所有当我们需要拿到当前的值时,我们可以使用useRef去保存我们需要获取的值,让后通过useRef上的current属性便可以拿到我们保存的那个值的最新的数据了。
- memo, useMemo, useCallback是用于对Hooks组件的一些行为进行优化的。我们在对组件进行更改或者刷新的时候,总是会去从新渲染组件,那么此时连带着我们的一些并未发生变化的子组件也会从新去执行渲染,那么当我们的渲染过于频繁时,就会有很大的性能损耗,那么我们的mome,useMemo,以及useCallback就可以解决这种频繁渲染的问题了。它们有点类似于类组件的shouldComponentUpdate的生命周期钩子,用来判断组件什么时候需要被从新渲染。
import React, {
useState,
useReducer,
useRef,
memo,
useMemo,
useCallback
} from 'react'
function countReducer(state, action){
switch (action.type){
case 'add':
return state + 1
case 'minus':
return state - 1
default :
return state
}
}
function MyCountFunc (){
const [count, dispatchCount] = useReducer(countReducer, 0)
const [name, setName] = useState('chen')
// 使用useMemo应传入的是一个function,
const config = useMemo(() => ({
text: `count is ${count}`,
color: count > 3 ? 'red' : 'blue'
}), [count])
const handleCount = useCallback(() => dispatchCount({type: 'add'}), [])
return (
<>
<input value={name} onChange={(e) => {setName(e.target.value)}}/>
<Child config={config} dispatchCount={handleCount}/>
<button onClick={handelAlertCount}>alert Count</button>
</>
)
}
const Child = memo(function Child({config, dispatchCount}){
console.log('child render')
return (
<>
<button type={{color: config.color}} onClick={dispatchCount}>{config.text}</button>
</>
)
})
export default MyCountFunc
此时,由于我们的MyCountFunc组件在其中的任何有个state发生变化时,都会去从新渲染我们的组件,而此时,我们的Child子组件由于使用的memo包裹,并且对于传递进入的参数值也使用的useMemo,useCallback进行处理,就会导致只有当我们的count值发生了变化时,我们的Child子组件才会去从新的执行.
useMemo的第一个参数仍旧是一个函数,表示我们的useMemo需要去执行的回调,其次,第二个参数仍旧是一个依赖的数组,当我们数组中的元素发生了变化时才去执行这个回调,这样就保证了我们这里的config参数要在每次count发生变化后才会去改变,就不会每次都发生变化了。其次,useCallback是用于对函数句柄进行绑定的,在函数中对应的值发生变化时,该函数不会去从新创建,而保证了每次传递的函数是一样的,这样对于子组件就没有任何的更新,所以,此时我们的子组件就不会去渲染。
至此,我们对于Hooks的一些常用的钩子就介绍完了,希望此文对有需要的人有一定的帮助.