前言
React Hooks 是React 16.7.0-alpha 版本推出的新特性。從 16.8.0 開始,React更穩定的支持了這一新特性。
它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。
注意:React 16.8.0 是第一個支持 Hook 的版本。升級時,請注意更新所有的 package,包括 React DOM。React Native 將在下一個穩定版本中支持 Hook。
如果說promise是JavaScript異步的終極解決方案,那么React Hooks從某種意義上來說,也是react狀態管理的終極解決方案。
為什么用 React Hooks?與其他狀態管理方案有什么區別?
在react hooks,react態管理方案主要有如下2種:
這兩種狀態解決方案其實已經很不錯,可以滿足絕大多數狀態共享的業務場景。 當然,像FB這樣優秀的團隊優秀的人總喜歡鑽研,不斷優化。認為這兩種狀態解決方案嵌套太多,很多業務邏輯可以抽象出來。不僅僅是共享狀態就完事了,還得共享出處理邏輯。為了減少不必要的組件嵌套寫法,實現更扁平、顆粒化的狀態 + 邏輯的復用,於是便推出了 React Hooks。
關於react hooks,具體可以多看看React Hooks官方文檔,理解會更深。
業務實戰1:實現一個簡單的顯示隱藏彈出框
1、基於 render-props實現:
// 將父組建的 on狀態 和toggle 事件,共享給嵌套下的子組件
function App() { return ( <Toggle initial={false}> {({ on, toggle }) => ( <Button type="primary" onClick={toggle}> Open Modal </Button> <Modal visible={on} onOk={toggle} onCancel={toggle} /> )} </Toggle> ) }
2、用react hooks實現
import React, { useState } from 'react'; function Example() { // 聲明一個新的叫做 “visible” 的 state 變量 const [visible, setVisible] = useState(false); return ( <div> <Modal onClick={() => setVisible(!visible)}> Click me </Modal> </div> ); }
業務實戰2: 接收傳入props后立馬更新自有的State狀態(received passed props and updated)
function Avatar(props) { const [user, setUser] = React.useState({...props.user}); // 這里僅僅是給了一個初始值,僅第一次渲染的時候生效,后續都不會再更新了。 // 用useEffect,第二個參數傳入一個數組字段props.user, // 表示每次只要傳入的props.user有變化,那么就觸發setUser這個操作更新狀態。 React.useEffect(() => { setUser(props.user); }, [props.user]) return user.avatar ? (<img src={user.avatar}/>) : (<p>Loading...</p>); }
由上述例子可以看到,我們用到了useEffect這個hooks。
Effect Hook 可以讓你在函數組件中執行副作用操作
什么叫副作用呢? 數據獲取,設置訂閱以及手動更改 React 組件中的 DOM 都屬於副作用。
如果實在理解不了副作用,你可以理解為,在我們在寫傳統的react class組件里面的生命周期鈎子函數componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合中處理的相關邏輯,就是副作用。
也就是說,任何你想在頁面首次加載完(componentDidMount),后續每次更新完(componentDidUpdate),亦或者是組件卸載前(componentWillUnmount),這三個勾子中加入你自己的邏輯處理,就得用到useEffect
業務實戰3:顯示后,倒計時自動隱藏(與setInterval結合)
業務場景具體如下:實現一個倒計時30秒,每秒鍾減1,然后倒計時完畢這個組件自動隱藏。
function App() { const [count, setCount] = useState(30); useEffect(() => { setInterval(() => { setCount(count - 1); }, 1000); }); return <div className="App">{count}</div>; }
看看上述的寫法,自己運行一下就只知道了,肯定是大錯特錯的。 最基本的,setInterval這個刪除定時器都沒有做相關處理。 其次,每次都會生成不同都setInterval實例。那么如何加以控制呢?
來看看正確都寫法,這里要用到useRef這種類型都hooks了。
function App(props){ const [visible,setVisible] = useState(false);// 初始化值為false,隱藏 const [count,setCount] = useState(0); // 倒計時字段,初始值為0 // 明確定時器要做什么:我們這里是每秒 -1,小於等於0的時候就自動給隱藏 // 同時,本此的跟新跟下次的渲染更新要共享狀態,記住上次更新這個count減到哪里了 // 基於上述問題,我們可以使用如下方案: // 聲明一個useRef對象,初始化為null const intervalCb = useRef(null); useEffect(()=>{ // useRef 對象有一個current屬性,可以給它賦值為一個函數 intervalCb.current = () => { if(count <= 0){ setVisible(false); }else{ setCount(count - 1); } }; }) // 在componentDidMount中設置一個定時器 useEffect(()=>{ function itvFn(){ // 在此前,定時器的回調函數已經聲明,這里可以直接調用 intervalCb.current(); } const itvId = window.setInterval(itvFn,1000); // return 一個回調函數,表示清除處理 return () => window.clearInterval(itvId); },[]) // 傳入要監聽的對象,空數組表示只監聽一次,相當於didMounted } return (<div className={visible}> {count} </div>) }
關於useRef 更多的請看useRef官方文檔
業務實戰4: 自定義hooks
基於上述setInterval的業務功能實現,我們簡單的來寫一個自定義的hooks
export default function useInterval(callback) { const savedCallback = useRef(); useEffect(() => { savedCallback.current = callback; }); useEffect(() => { function tick() { savedCallback.current(); } let id = setInterval(tick, 1000); return () => clearInterval(id); }, []); }
然后在你需要用到的地方引入:
import useInterval from 'xx/self_hooks'; function App(){ useInterval(()=>{ // ... 跟useEffect寫法類似,目前只支持傳入一個function類型的參數,如果要接收多個參數,那么在自定義hooks那里去自行處理一下。 }) }
關於自定義hooks,更多請查看自定義hooks
暫結
關於react hooks 簡單的使用,此篇就暫時寫到這里,當然,復雜一點的,可以使用useReducer、useContext和useEffect代替Redux方案。后續我們應該單獨來研究這塊。 回顧一下,如何才能說我們會用react hooks 呢?
- 第一、明白概念,干什么用的,解決哪些痛點?
- 第二、useEffect傳入幾個參數?分表表示聲明?useRef作用是什么?這些可以解決我們基本的業務需求。
- 第三、useReducer、useContext 怎么替換 redux?
- 讀源碼,搞清楚為什么useEffect不能卸載if嵌套中等之類的問題?
今天先把問題拋出,后續按照這個,循序漸進的學習掌握react hooks。