如果你覺得可以,請多點贊,鼓勵我寫出更精彩的文章?。如果你感覺有問題,也歡迎在評論區評論,三人行,必有我師焉
如果是你一個React
開發者,或多或少接觸了React 16.8
的一些新特性。例如:React.memo()
、React.lazy
、React.Suspense
等一些比較好的新特性。但是,其中我認為,最爽的是React-Hooks
的提出。
這意味著啥,React
專案組,對效能要求更加嚴格(Hooks
推崇的是函式元件
,在渲染速度上是比類元件
快不少)。不管你是否是一個React
開發的老鳥,但是在實際專案開發中,肯定見過讓人頭疼的程式碼堆砌。
尤其在一些公司早期的專案程式碼中,元件化的思維很匱乏,直接是按一個頁面一個元件。一個生命週期的方法中巢狀著5-10個不相關的程式碼。原來我在某東的時候,有的元件甚至是1000行程式碼,就是純粹的去堆砌一些邏輯。久而久之,就變成了一些魔鬼程式碼,讓人望而生畏。
而Hooks
的出現,對一些魔鬼程式碼的出現,有了一些制約。(其實之所以會出現魔鬼程式碼,還是由於研發團隊對如何進行元件劃分或者是邏輯複用的認知度不夠而導致的)。
雖然,不用Hooks
,利用HOC
/Render Props
也可以實現元件化和邏輯複用。但是在實際專案開發中,發現不管是HOC
還是Render Props
將元件進行多次巢狀,就會陷入wrapper hell
。如果你開發中用過React-dev-tools
看頁面。就會發現,一個簡單的元件,被層層包裹。那場面簡直是車禍現場,慘不忍睹。
和大家墨跡了半天,算是對現有的React
的開發模式的吐槽吧。客官,不要著急離開,這就正式進入正題。
今天帶大家來看看Hooks
中比較基礎的API:useState/useEffect
。至於像其他的自定義hook,會專門有一篇文章來給大家詳細講述。
順便提一句,這篇文章只是一個Hooks
的簡單介紹和入門。這個技術方案是針對函式元件的。想了解為什麼FaceBook構建了這個技術方案,或者是解決了什麼技術痛點。
可以參考官網
TL;DR
- Hooks到底是啥?
- State Hook
- useState的語法解析
- useState的返回值
- 構建多個State Hooks
- Effect Hook
- 有條件的呼叫Effect Hook
- componentWillUnmount()何時呼叫
- 同時使用State和Effects
- 彙總
Hooks到底是啥?
React Hooks
是將React.Component
的特性新增到函式元件的一種方式。
例如將
- State
- 元件生命週期
Hooks能夠讓開發者不使用
class
來使用React
的特性
可能大家會問,是不是以後React
官網就不會繼續維護用class
來構建元件的方式了。這一點大可不必擔心。
同時也有一點需要大家清楚就是:Hook
的出現只是新增了一種處理邏輯的方式,而不是讓你將原有的類元件重寫為函式元件。
State Hook
假如我們有如下的元件
傳統的類元件
import React, { Component } from 'react';
class JustAnotherCounter extends Component {
state = {
count: 0
};
setCount = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.setCount}>計算</button>
</div>
);
}
}
複製程式碼
利用useState
在函式元件中使用state
import React, { useState } from 'react';
function JustAnotherCounter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>計算</button>
</div>
);
}
複製程式碼
useState的語法解析
你可能對useState()
的語法不是很熟悉。但是發現,它是使用了陣列的解構語法。這就像我們通過物件的解構從某個物件中剝取一些特定屬性一樣。
讓我們通過比較物件的解構和陣列的解構,來看看useState
是如何選擇了陣列的解構。
物件解構
const users = { admin: 'chris', user: 'nick' };
// 獲取admin和user並且為他們起一個別名
const { admin: SuperAdmin, user: SuperUser } = users;
複製程式碼
在進行物件結構的時候,如果需要對解構的屬性起一個別名,就需要用額外的變數去接收。而我們通過陣列結構的話,我們只需要定義需要接收陣列值的變數即可。第一個變數就是陣列中的第一個值
陣列解構
//
const users = ['chris', 'nick'];
// 獲取值的同時為值起了別名
const [SuperAdmin, SuperUser] = users;
複製程式碼
useState的返回值
useState
返回了兩個變數,並且通過上文的分析,我們可以給這兩個變數隨意起名字。
- 第一個變數其實一個值,類似於類元件中的
this.state
- 第二個變數是一個函式,用於更新第一個變數的值。類似於類元件中的
this.setState
在我們呼叫useState
的時候,傳入了一個值,這個值是作為第一個變數的初始值。
構建多個State Hooks
由於在實際開發中一個邏輯片段不可能細分到只有一個state
去維護和控制,所以Hooks
支援在一個函式元件中,多次呼叫useState
新增多個狀態值。
import React, { useState } from 'react';
function AllTheThings() {
const [count, setCount] = useState(0);
const [products, setProducts] = useState([{ name: 'Surfboard', price: 100 }]);
const [coupon, setCoupon] = useState(null);
return <div>{/此處可以隨意使用已經構建的狀態值/}</div>;
}
複製程式碼
Effect Hook
State Hook
讓我們可以在函式元件中使用state
,讓函式元件在使用上變得更加靈活。而Effect Hook
使得函式元件擁有了生命週期方法。
函式元件中的Effects其實等同於類元件的
componentDidMount
、componentDidUpdate
和componentWillUnmount
的結合體
字如其名,Effect
就是維護一些具有副作用的操作
- 獲取遠端介面資料
- 操作DOM
- 響應訂閱操作
Effects
在每次render
之後觸發,不管元件是否是首次渲染
具有副操作的類元件
import React, { Component } from 'react';
class DoSomethingCrazy extends Component {
componentDidMount() {
console.log('我要起飛了!');
document.title = '這是標題';
}
render() {
return <div>做點瘋狂的事</div>;
}
}
複製程式碼
用useEffect
修改元件
function DoSomethingCrazy() {
useEffect(() => {
console.log('我要起飛了');
document.title = '這是標題';
});
return <div>做點瘋狂的事</div>;
}
複製程式碼
通過比較,是不是感覺利用useEffect
在程式碼量上還有邏輯捆綁上比傳統的元件都具有很大的優勢。
有條件的呼叫Effect Hook
由於useEffect()
在每次render
的之後,總會被呼叫。那我們是否有一個方式,只限制它在首次載入時呼叫。
其實Effect Hook接收第二個引數(Array型別)。只有陣列中的值發生變化了,才會呼叫useEffect()
而不是每次在渲染的時候呼叫。
componentDidMount: 只執行一次
// 當第二個引數為空陣列([])的時候,只是被呼叫一次(也就相當於類元件中componentDidMount)
useEffect(() => {
// 值執行一次
}, []);
複製程式碼
componentDidUpdate: 根據值是否變化來執行
// 只有count變化才執行
useEffect(
() => {
//只有count變化才執行
},
[count]
);
複製程式碼
componentWillUnmount()何時呼叫
我們上文說過,函式元件中的Effects其實等同於類元件的componentDidMount
、componentDidUpdate
和componentWillUnmount
的結合體。而通過控制第二個引數的值,可以模擬componentDidMount
、componentDidUpdate
。但是componentWillUnmount
如何才會呼叫呢。
我們只需要在useEffect()
中返回一個函式,既可以模擬componentWillUnmount
的功能,也就是在元件unmounts時呼叫。
useEffect(() => {
UsersAPI.subscribeToUserLikes();
// unsubscribe
return () => {
UsersAPI.unsubscribeFromUserLikes();
};
});
複製程式碼
同時使用State和Effects
既然在函式元件中可以單獨使用useState
來模擬類元件的this.state
的功能,用useEffect
來模擬類元件的生命週期。那我們可以同時利用useState
和useEffect
將函式元件變成有狀態元件。
我們通過一個真實的例子來講解一下如何共同使用這些API。我們通過useEffect()
來獲取GitHub API
並且用useState()
來儲存。
使用useState
import React, { useState } from 'react';
function GitHubUsers() {
const [users, setUsers] = useState([]);
}
複製程式碼
我們通過useState([])
的引數將users
的值,賦為[]
。
使用useEffect
來獲取資料
import React, { useState } from 'react';
function GitHubUsers() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('https://api.github.com/users')
.then(response => response.json())
.then(data => {
setUsers(data); // 為users賦值
});
}, []); //通過useEffect的第二個引數,來約定,該操作只被呼叫一次
}
複製程式碼
資料展示
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
function GitHubUsers() {
// 剛才的程式碼
return (
<div className="section">
{users.map(user => (
<div key={user.id} className="card">
<h5>{user.login}</h5>
</div>
))}
</div>
);
}
複製程式碼
這樣一個簡單的Hooks
的例子就完成了。
彙總
上面的分析步驟,只是簡單介紹了useState
/useEffect
的使用方式,我相信大家在使用過程中,肯定遇到了比這還複雜的。但是,萬變不離其宗。都是利用hook
的一些API。為函式元件賦予狀態。
還有一點,因為在使用useState
或者useEffect
的時候,我們可以將相關的程式碼進行統一處理,這樣不管在業務開發還是程式碼維護上,變的很清楚。而不是像原來的開發一樣,一個生命週期包含著無數的不相干的邏輯程式碼。
並且,hooks
還支援自定義。本人認為,這才是hooks
的真正強大之處。通過自定義一些業務程式碼,真正的實現邏輯複用。可以有效的減少wrapper hell
。讓程式碼實現切片化程式設計。