Hooks是React 16.8中的新增功能。它們允許您在不編寫類class的情況下使用狀態(tài)state和其他React功能。
要使用Hooks請先升級你的React包,包括ReactDom。
為什么使用Hooks
我們總是會遇到某些困惑,比如:
- 當我們定義的一個組件只用到一兩個
state狀態(tài)變量 - 一個組件只用到用到了生命周期某一個方法例如
componentDidMount、componentWillUpdate、componentWillMount
為了解決上面的問題,我們不得不定義一個class component,來達到我們的目的,看起來代碼有很多的冗余。而hooks就是幫我們解決這些問題,讓我們可以拋棄class component擁抱function component,使代碼看起來清晰,整潔。
如何使用useState
引用官方的一個例子:
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
看一下上面做了什么操作:
- 定義了一個名稱為
Example的function component - 使用
useState在這個方法內(nèi)部定義了一個變量count - 并暴露了一個修改
count的方法setCount - 每次點擊按鈕對
count遞增
如果我們用class component是如何實現(xiàn)上面的步驟的呢?等效于:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
一對比我們會發(fā)現(xiàn)使用Hooks能讓代碼看起來更加有條理,簡潔,也能使狀態(tài)和UI層分離
當然Hooks能讓我們可以定義多個狀態(tài),每個狀態(tài)都是獨立的
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
同時我們會想到如果有很多個狀態(tài),我們要寫多個useState,Hooks支持狀態(tài)值是對象,我們也可以這樣寫:
const [data, setData] = useState({age: 42, fruit: 'banana', todos: [{ text: 'Learn Hooks' }]});
需要注意的是,上面的方括號[data, setData]并不是Hooks的語法,而是JavaScript的數(shù)組結構
如何使用useEffect
我們常常需要在組件加載的時候就需要運行某些函數(shù),異步請求數(shù)據(jù),我們可能會這樣寫:
import { Component } from 'react';
import axios from 'axios'
class Exampel extends Component {
constructor(props) {
super(props);
this.state = {
data: null
};
}
getItem = () => {
axios
.post('請求url')
.then(res => {
this.setState{
data: res.data
}
})
.catch(err => console.error(err))
}
componentDidMount () {
this.getItem()
}
//...
}
使用Hooks的useEffect我們可以這樣寫:
import { useState, useEffect } from 'react'
import axios from 'axios'
function Example(){
const [data, setData] = useState([]);
function getItem () {
axios
.post('請求url')
.then(res => {
setData(res.data)
})
.catch(err => console.error(err))
}
useEffect(() => {
getItem()
}, [])
//...
}
當然我開始也是這樣寫的,毫無疑問這樣寫目前看起來沒有什么問題,能達到預期的效果。
但是有時候我會發(fā)現(xiàn)使用useEffect的時候,里面的函數(shù)被無限循環(huán)調(diào)用了,這當然和我想象的初始化的時候只執(zhí)行一次出入很大,這是為什么呢?
我們來看個例子:
在發(fā)送請求的時候,往往我們會添加一些參數(shù):
import { useState, useEffect } from 'react'
import axios from 'axios'
function Example(){
const [data, setData] = useState([]);
const [params, setParams] = useState({});
function getItem () {
axios
.post('請求url', params) //注意我在這里使用了另一個狀態(tài)params
.then(res => {
setData(res.data)
})
.catch(err => console.error(err))
}
useEffect(() => {
getItem()
}, [getItem]) //在參數(shù)改變的時候調(diào)用
//...
}
上面的例子,我在useEffect 里的依賴函數(shù)getItem又對返回函數(shù)data賦值,使得每次data改變的時候useEffect就會被觸發(fā),導致了無限循環(huán)。
解決方法就是把useEffect的第二個參數(shù)改成[]也就是useEffect(() => {....}, []),這意味著效果函數(shù)應該被調(diào)用一次:僅在第一次裝載/渲染之后。但是這并不符合Hooks規(guī)范?;蛘呤悄阋男Ч褪莗arams改變后再次調(diào)用請求,用useCallback看看能否解決問題。
import { useState, useEffect, useCallback } from 'react'
import axios from 'axios'
function Example(){
const [data, setData] = useState([]);
const [params, setParams] = useState({});
const getItem = useCallback(() => { //使用useCallback解決依賴函數(shù)的不可控變量
axios
.post('請求url', params) //注意我在這里使用了另一個狀態(tài)params
.then(res => {
setData(res.data)
})
.catch(err => console.error(err))
}, [params, setData])//在useCallback里檢查變量的改變
useEffect(() => {
getItem()
}, [getItem]) //符合useEffect規(guī)范
//...
}
我們推薦使用上面這種方法解決避免useEffect無限循環(huán)
無限循環(huán)調(diào)用產(chǎn)生的原因主要有以下幾種,你可以一個個排除看看:
- 是不是在依賴函數(shù)里重新設置了狀態(tài)
- 是什么操作引起了父組件重新render,導致本身組件也被重新加載,引起無限循環(huán)
-
useEffect是不是被放在了判斷if語句或者循環(huán)for語句里面了
當然我們還這樣使用:
useEffect(() => {
function getItem () {
axios
.post('https://www.easy-mock.com/mock/5b4eb12d56c59c6f56266215/api/order_list', params) // 注意我在這里使用了另一個狀態(tài)params
.then(res => {
setData(res.data)
})
.catch(err => console.error(err))
}
getItem()
}, [params, setData])
將useEffect所有依賴的函數(shù)放在useEffect里面,讓useEffect變得自給自足,這樣我們減少了很多不可控制的因素,方法里面所有的依賴項都是可見的可控的。
還有一些Hooks提供的方法useRef、useCallback等好用的方法可以查看官網(wǎng)API https://reactjs.org/docs/hooks-reference.html
總結:使用Hooks的好處:
- 可以讓我們放棄
class component,擁抱function component - 可以在
function component使用狀態(tài)且是獨立的狀態(tài),可以獨立維護,這和狀態(tài)組件有著很大的區(qū)別 - 可以讓我們一個個組件去做優(yōu)化,不需要全部重構
- 使狀態(tài)與
UI分離 - 讓我們的代碼看起來更加有條理、清晰、簡潔
tips: 簡書上交流可能會看不到消息,如有問題,歡迎進群交流50063010
參考鏈接1:https://reactjs.org/docs/hooks-intro.html
參考鏈接2:https://overreacted.io/a-complete-guide-to-useeffect/