簡述
Hooks?
hooks 是 React v16.7.0-alpha 加入的新特性,可以在 class 外使用 state 以及其他 React 新特性欠橘。
優(yōu)點
- 相比類來說焚刺,相似的邏輯不再分隔開联四,代碼更簡潔
useState
- 和類的對比
一個計數(shù)小例子(來自官網(wǎng)~)
類方法實現(xiàn):
import React from 'react';
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<>
<div>You clicked {this.state.count} times</div>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>add</button>
<button onClick={() => this.setState({ count: this.state.count - 1 })}>reduce</button>
</>
);
}
}
export default Example
useState 實現(xiàn):
import React,{ useState } from 'react';
import styles from './main.module.less';
function Example() {
const [count,setCount] = useState(1);
return (
<>
<div className={styles.container}>you clicked {count} times</div>
<button onClick={()=>setCount(count+1)}>add</button>
<button onClick={()=>setCount(count-1)}>reduce</button>
</>
);
}
export default Example;
- useState
const [count,setCount] = useState(1);
// useState 返回的是一對值逗宜,如下:
var countStateVariable = useState(1);
var count = countStateVariable[0]; // 第一個元素
var setCount = countStateVariable[1]; // 第二個元素
// 使用的是js的"數(shù)組破壞",官方解釋使用[0]和[1]會有很大的迷惑性~
多個 state 怎么寫?我們可以使用多個 useState.
function Example() {
const [count,setCount] = useState(1);
const [age,setAge] = useState(10);
const [fruit,setFriute] = useState('apple');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
return (
<>
<div className={styles.container}>you clicked {count} times</div>
<button onClick={()=>setCount(count+1)}>add</button>
<button onClick={()=>setCount(count-1)}>reduce</button>
</>
);
}
那么React是怎么記住"哪個狀態(tài)對應哪個 useState"? 答案就是依賴調(diào)用鉤子函數(shù)的順序鳄梅。所以存在了幾條規(guī)范。
useEffect
useEffect 允許你在功能組件里面執(zhí)行副作用
- 和類的對比
類方法實現(xiàn):
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
data:{},
};
}
componentDidMount(){
fetch('/api/xiaoyi/goodsList').then(res=>res.json()).then(data=>{
console.log(data,'data')
setState({data})
})
window.addEventListener('resize', this.getHeight);
}
componentDidUpdate(preProps){
if(preProp.id!==this.props.id){
fetch('/api/xiaoyi/goodsList').then(res=>res.json()).then(data=>{
console.log(data,'data')
})
}
}
componentWillUnmount() {
window.remoteEventListener('resize', this.getHeight);
}
render() {
return (
<>
<div>You clicked {this.state.count} times</div>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>add</button>
<button onClick={() => this.setState({ count: this.state.count - 1 })}>reduce</button>
</>
);
}
}
export default Example
useEffect 實現(xiàn):
import React,{ useState, useEffect } from 'react';
import styles from './main.module.less';
function Example(props) {
const [count,setCount] = useState(1);
useEffect(()=>{
fetch('/api/xiaoyi/goodsList').then(res=>res.json()).then(data=>{
console.log(data,'data')
})
window.addEventListener('resize', ()=>{console.log(111)});
return () => {
window.removeEventListener('resize', ()=>{console.log(111)});
}
},[props.id])
return (
<>
<div className={styles.container}>you clicked {count} times</div>
<button onClick={()=>setCount(count+1)}>add</button>
<button onClick={()=>setCount(count-1)}>reduce</button>
</>
);
}
export default Example;
Hooks允許我們根據(jù)代碼所做的而不是生命周期方法名來分割代碼未檩。
useEffect 的參數(shù)是一個函數(shù)戴尸,組件每次渲染之后,都會調(diào)用這個函數(shù)參數(shù)冤狡,這樣就達到了 componentDidMount 和 componentDidUpdate 一樣的效果孙蒙。
useEffect 第一個參數(shù)返回的一個函數(shù)相當于清除該副作用,會在每次渲染執(zhí)行筒溃。
為什么不在組件銷毀的時候執(zhí)行马篮?<br /> 因為如果要銷毀的內(nèi)容中間發(fā)生改變,在銷毀的時候怜奖,銷毀的就是之前的內(nèi)容浑测,不是當前要銷毀的了,就會產(chǎn)生bug。
useEffect 的第二個參數(shù)迁央,代表重新渲染如果這個值未發(fā)生變化掷匠,就跳過渲染。
如果要運行一個 effect 并只清理一次(在裝載和卸載時)岖圈,可以將空數(shù)組([])作為第二個參數(shù)傳遞讹语。這告訴react,您的效果不依賴于來自props或state的任何值蜂科,因此它不需要重新運行顽决。這并不是一個特殊的情況——它直接來自于輸入數(shù)組的工作方式。
rules
使用 hooks 的規(guī)則
- 不要在循環(huán)导匣,條件或嵌套函數(shù)內(nèi)使用鉤子函數(shù)才菠。相反,要在函數(shù)頂層使用鉤子贡定。
- 不要在常規(guī)的js函數(shù)里面使用鉤子函數(shù)
- 可以自定義的鉤子函數(shù)里面使用
<a name="WluJO"></a>
react 提供了esLint插件 [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks)
來幫助我們執(zhí)行這些規(guī)則
- npm install eslint-plugin-react-hooks@next
規(guī)則由來
由上面可知赋访,單個組件使用多個 State 或者 Effect 鉤子函數(shù)時候,React 依賴調(diào)用鉤子函數(shù)的順序記住相對應的狀態(tài)的缓待。所以當使用條件判斷時候:
// 打破了第一條規(guī)則
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
當?shù)谝淮蝦ender,name!==''為true的時候蚓耽,這個鉤子函數(shù)會執(zhí)行;當下一次為false的時候旋炒,渲染就會跳過這個鉤子函數(shù)步悠。此時鉤子的調(diào)用順序發(fā)生了改變。(加入這個鉤子函數(shù)之前和之后都存在其他鉤子函數(shù))
useState('loving')
// useEffect(persistForm) // 這個 hook 將會被跳過
useState('lili') // 現(xiàn)在讀取是2国葬,但是之前是3贤徒,讀取這個狀態(tài)變量就會出錯。
react不知道第二次useState hook調(diào)用返回什么汇四。react預期此組件中的第二個hook調(diào)用與PersisteForm效果相對應接奈,就像在上一次呈現(xiàn)期間一樣,但它不再如此通孽。從那時起序宦,我們跳過的鉤子調(diào)用之后的每一個鉤子調(diào)用也會一個接一個地移動,從而導致錯誤背苦。
useEffect(function persistForm() {
// ??不會打破第一條規(guī)則
if (name !== '') {
localStorage.setItem('formData', name);
}
});