這篇文章主要講Hooks,若是你想了解React 16的其餘新特性,請移步 React 16新特性全解 (上), React 16新特性全解 (中)html
Hooks是什麼?react
咱們知道,functional component在使用的時候有一些限制,好比須要生命週期、state的時候就不能用functional component。 而有了Hooks,你就能夠在funtional component裏,使用class component的功能:props,state,context,refs,和生命週期函數等等。npm
雖然Hooks已經有要取代正宮class的趨勢了,可是react目前沒有計劃拋棄class,因此不要慌,你仍是能夠跟往常同樣使用class。bash
在真正介紹Hook以前,仍是同樣先來了解爲何要引入Hooks?其實不僅僅是爲了給functional component賦於class component的功能。app
還有下面的問題:函數
1.組件之間很難複用邏輯工具
以前若是咱們須要複用邏輯,經常使用的兩種方式是render props 跟 higher-order components。可是這兩種方式都須要你重構代碼,因此比較麻煩。學習
最重要的是,用這兩種方式的話,在React Devtools裏,會看到不少的嵌套組件。fetch
在這個圖能夠看到Header外層套着不少嵌套組件。ui
2.複雜組件很難理解
在以前的class component裏,咱們的生命週期函數裏一般放着不相關的代碼,而相關的代碼確要放在不一樣的生命週期函數裏。這樣說可能有點繞,咱們來看一個具體的例子。
class App extends React.component {
componentDidMount() {
window.addEventListener('scroll', () => {console.log('a')})
this.fetchData();
}
componentDidUpdate() {
this.fetchData();
}
componentWillUnmount() {
window.removeEventListener('scroll', () => {console.log('a')})
}
render() {
return <div>ddd</div>
}
}
複製代碼
這應該是咱們平時會常常寫的代碼。在componentDidMount裏作事件綁定,獲取數據。在componentWillUnMount裏解綁事件。
這樣作的問題是:componentDidMount裝着的代碼都是不相關的,而相關聯的事件綁定以及事件解綁,分散在componentDidMount 跟 componentWillUnMount裏。這樣若是組件的邏輯越寫越複雜以後,就會變得很難維護易出bug。
後面講Effect Hook的時候,我會介紹這個問題用Hooks怎麼解決。
3.class比較難學
React團隊發現class是初學者學習React的大障礙。要學習class component,你必需要知道幾點:
理解了Hooks誕生的緣由,接着來看看要如何使用。
假設我須要實現一個功能,點擊app時候,count數目加一。用class的形式實現就是 這樣的:
Class:
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>
);
}
}
複製代碼
Hooks:
若是須要用Hooks實現,變成
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> ); } 複製代碼
在這裏demo中。useState就是Hook。咱們經過它來在function component里加入state數據。
二者對好比下:
1. 定義state變量
Class:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
複製代碼
Hooks:
import React, { useState } from 'react';
function Example() {
// 經過useState這個Hooks定義state變量:count。而且經過useState給count賦初始值0,只在初始化時候使用一次
const [count, setCount] = useState(0);
複製代碼
在function component裏,咱們是沒有this的。因此沒辦法向Class那樣用this來建立state,這時候Hooks閃亮登場。 經過useState這個hooks咱們能夠定義count這個state變量。 由Hooks定義的state變量不必定要是object,能夠是string、number。傳入的內容至關於給變量賦初始值。
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
複製代碼
2. 渲染state
Class:
<p>You clicked {this.state.count} times</p>
複製代碼
Hooks:
<p>You clicked {count} times</p>
複製代碼
能夠不須要用this,直接使用count
3.更新state
Class:
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
複製代碼
Hooks:
<button onClick={() => setCount(count + 1)}>
Click me
</button>
複製代碼
咱們能夠看到 const [count, setCount] = useState(0);
useState返回兩個參數,一個是當前state的值,還有一個實際上是一個函數,用來改變state的值,就是setCount。
相似setState,可是不一樣的是,它不會將舊的state跟新的state合併在一塊兒,而是覆蓋式的重寫state的值。
說完了functional component裏面如何使用state以後,咱們再來看如何用Effect Hook來取代生命週期。
通常咱們都會在生命週期componentDidMount, componentDidUpdate與 componentWillUnmount中作一些反作用的操做,例如:獲取數據,訂閱,手動改變DOM。而在hooks裏,這些生命週期函數都被統一成一個方法 useEffect。
下面咱們來舉個例子:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
複製代碼
在這個例子中,咱們在useEffect 裏完成了反作用的操做。demo演示
Effects Hooks 就在functional component裏, 因此它能夠直接訪問props跟state。 React在每次render以後都會調用effect,包括第一次render。
可是這裏還遺留兩個問題
一、咱們在開篇說到,class component有個問題就是生命週期函數裏的代碼都是不相關的,而相關的代碼確要被打散在不一樣的生命週期函數裏。這個問題用Hooks的話就能夠解決。好比綁定、解綁事件,在使用class的時候,在componentDidMount裏 監聽了一個事件,以後須要在componentWillMount裏給它解綁。
用Hook只須要在useEffect一個函數就能夠作到。它能夠經過返回一個函數來專門作清除的工做,代碼以下:
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// 在這裏返回一個函數來作這件事
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
複製代碼
在這個case中,unsubscribeFromFriendStatus不只僅會在組件unmount的時候 調用,同時在從新渲染的時候也會調用。
若是你只想useEffect只在mount與unmount時候調用,須要這樣傳一個[] 做爲第二個參數。
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}, []);
複製代碼
二、有時候咱們並不想每次state的改變,都去調用useEffect。 在class裏,會這樣作
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
複製代碼
在Hooks,能夠經過給useEffect傳入第二個參數,即它關注的state變量,來作到這件事。
例如:當count改變時候,纔去調用useEffect。
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
複製代碼
注意事項:
說了那麼多,總結如下,其實Hooks就是幫助咱們在function component裏直接使用原來class component纔有的特性。
以上,咱們只接觸到了兩種hooks,還有更多好比useContext, useReducer, useCallback,感興趣的同窗能夠本身看下~
最後是使用Hooks的一些建議:
一些建議
No,react團隊不推薦用hooks從新寫一遍。 推薦作法是新的組件能夠直接使用,而後須要改老組件代碼的時候在順便改就好了。s
Hooks可讓你使用在不須要寫class的狀況,只用
React DevTools對hooks已經支持。同時強烈推薦安裝hooks的eslint校驗庫 eslint-plugin-react-hooks 。
Reac團隊下一步計劃
由於如今React Hooks還不能徹底cover全部class的功能,雖然他們已經很相近了。目前爲止Hooks不支持兩個不常使用的API getSnapshotBeforeUpdate 跟 componentDidCatch,可是React團隊正在努力,將來會支持的。