簡介
不久前,react在新的16.7.0-alpha.0版本中推出了新的hooks函式,其作用就是讓你可以不用類元件就可以使用react的state和其他功能。大家都知道,class的寫法有的時候很繁瑣,比如其中的this問題等等。本篇文章主要介紹函式包括這幾個useState
、useEffect
、useContext
、useRef
,這篇文章主要介紹一下這些函式的作用。
useState
比如說有一個元件需要內部維護自己的狀態,之前我們寫class元件的話一般都是定義一下state,然後需要修改的時候,用setState去修改自身的狀態。useState這個函式就是起到了這個作用,接受一個引數initialState
,也就是初始的state,同時返回一個陣列,第一個是當前的state,第二個就是相當於以前class元件的setState,每次呼叫這個函式都會更新state同時讓當前元件重新渲染一遍,看一下簡單的count計數器用hooks怎麼寫
function Cunter() {
const [count, setCount] = useState(0);
const [title, setTitle] = useState('hello-react');
console.log('render ===============');
return ( <
div className="count-box">
<
h1 className="title">
點了{count
}次<
/h1>
<
button onClick={() =>
{
setCount(count + 1);
setTitle('hello-hooks');
}
}>
Click Me! <
/button>
<
p>
{title
}<
/p>
<
/div>
)
}複製程式碼
上面這個元件在點選button後,開啟控制檯看到兩次更改狀態只render了一次,所以這裡的useState也是會將一次迴圈中的state變化合並,然後一起更新。同時在function元件中,useState可以呼叫多次,但不意味著一定要這樣寫,當然,initialState可以是陣列也可以是物件,所以你可以在一次set函式中直接修改像class元件中呼叫setState一樣,不過這樣寫的話,我覺得語義更加清晰。
useEffect
使用這個函式你可以通知react在當前元件每次渲染完成之後需要完成什麼動作,我覺得其作用可以理解為與之前的componentDidMount
和componentDidUpdate
兩個生命週期函式類似。這個函式支援兩個引數,第一個引數是一個函式,會在每次render之後執行,同時這個函式也可以返回一個函式,這個函式會在元件解除安裝後執行,類似於類元件的componnentDidUnmount
生命週期函式。舉個例子,比如說我們想實時獲取視窗的寬度,然後在這個元件被解除安裝的時候取消這個功能。看下程式碼
function Width() {
const [width,setWidth] = useState(window.innerWidth);
const setWidthFn = () =>
{
setWidth(window.innerWidth);
};
useEffect(() =>
{
window.addEventListener('resize',setWidthFn);
return () =>
{
window.removeEventListener('resize',setWidthFn)
}
});
return( <
div>
<
h1>
當前視窗寬度{width
}px<
/h1>
<
/div>
)
}複製程式碼
但是這樣也有缺點,就是每次render的時候都會執行一遍這個函式,有的時候我們並不希望都去執行,比如說一個詳情元件在第一次render的時候會跟根據props傳遞過來的id去取值,如果這樣寫的話,每一次props改變的時候都會引發元件,重新請求一遍,不過,react已經幫我們做好了處理,我們可以給useEffect函式傳遞第二個引數,****** 這樣,請求詳情的這個函式就只會在id改變的時候執行,所以這裡的程式碼可以這樣寫。
function SomeComponent({id
}){
useEffect(() =>
{
doSomethingWith(id) /* 當然,第二個引數可以傳遞進不只一個依賴項,這樣就是告訴React多個依賴項有一個發生變化就會重新執行這個函式。 也可以傳遞進一個空陣列,這樣是告訴React這個函式只執行一次。 具體為什麼要傳遞進去陣列,官網文件上沒有看到相關的解釋。 */
},[id])
}複製程式碼
useRef
這個函式同樣會接受一個initialState作為引數,同時返回一個物件,其中的current就是你當前傳遞進去的initialState,你可以把返回的物件作為ref屬性,傳遞給子元件來獲取DOM物件,同時你也可以用它來儲存previousProps,用法如下:
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() =>
{
prevCountRef.current = count;
});
const prevCount = prevCountRef.current;
return ( <
div>
<
h1>
Now: {count
}, before: {prevCount
}<
/h1>
<
button onClick={setCount(count + 1)
}>
Click Me!<
/button>
<
/div>
)
}複製程式碼
useContext
用法很簡單,將React.createContext返回的結果傳入useContext即可,同時context改變的時候也會引發該元件重新渲染。
useReducer
這個函式簡直和redux一模一樣了,不多介紹。
custom hooks
原本class元件中對於state的變化、修改,我們很難可以把關於state的邏輯抽象出來,實現複用,但是基於hooks函式,你就完全可以做到這一點。同時,React官網也說了useYourImagination,所以具體如何複用要結合實際專案進行抽離。
注意
- hooks函式可以讓你用function元件來完成很多class元件的生命週期函式,但是目前還不支援
getSnapshotBeforeUpdate
和componentDidCatch
,但是不能在class函式元件中使用,同時也不建議一個專案裡面兩者混用,保持團隊的一致性。 - hooks函式在函式元件中最好寫在Top Level,當然也不是非要這樣,主要是要保證每次元件render的時候都寫入一致的hooks函式,不要根據某個條件判斷決定是否要使用hooks。
寫在最後
最後引用一下Dan在React Conf上關於Hooks的演講,簡單翻譯一下。
我開始學習react的時候就想過React的logo和react有什麼關係,這個專案也不叫做Atom(原子),也不是一個物理引擎,其中的一個解釋是基於reaction(反應)的,化學反應就是基於原子在其中的表現,所以叫react。但是,我發現了一個更合理的解釋,我覺得是這樣,原子的種類和屬性決定了物理反應的表現和形態,react讓我知道了你可以把使用者介面分離成一個個獨立的個體,這個個體叫做元件,這些元件的屬性和種類決定了使用者介面的外觀和特效。可是搞笑的是,Atom這個詞的本身含義就是不可分離的,當科學家們第一次發現原子的時候,他們以為這就是最小的物質,但不久他們就發現了電子,電子就是原子中更小的一部分,電子對原子如何表現進行了更深一層的解釋。我覺得hooks就像是電子一樣,他不是一個新的功能,他只是讓我能夠使用那些react已知的功能,比如說state(狀態),context(上下文),life cycle生命週期函式,hooks是react一種更直接的表達方式,更好的解釋了元件如何在內部工作的,我覺得這些已經隱藏了長達四年久,現在你看一下react的logo,你可以看到那些電子的運動軌道,所以說hooks可能就一直存在,就像logo上的電子軌道一樣。