原文地址:github.com/hentaicrack…javascript
React Hooks
在 React@16.8 版本正式發佈。我最近在一兩個公司的內部項目中也開始用起來嚐嚐鮮。html
不瞭解 Hooks
的同窗先擼一遍文檔。本文不對 Hooks
作詳細介紹,只闡述一種使用 Hooks
的思路。vue
通常咱們寫 React
若是不是特別大的應用,先後端數據交互邏輯不復雜,這樣咱們直接按照正常流程寫組件就能知足簡單的業務場景。隨着業務場景的深刻漸漸地咱們組件變大變多,組件與組件之間的數據通信(也就是狀態管理,不過我更願意稱之爲數據通信)變得愈來愈複雜。因此咱們引入了 Redux
來維護咱們日趨複雜的數據通信。java
秉承着這種思路,我在開發應用的時候是沒有一開始就引入 Redux
,由於一開始我以爲就是個小項目。隨着深刻項目的開發,其實並無這麼簡單。react
可是也沒有太複雜,這時我把眼光放到了 Context
身上。Context
本意是上下文,它提供一個 Provider
和一個 Consumer
,這裏和 Angular
裏的 Provider
有點相似,也就是生產者/消費者模式,在某個頂層提供一個 Provider
,下面的子元素經過 Consumer
來消費 Provider
裏的數據和方法。git
經過這個概念,咱們把不一樣層級裏的組件共享同一個頂層 Provider
,而且組件內部使用 Consumer
來消費共享數據。github
當咱們能共享數據後,還剩一個問題就是如何更改 Provider
裏的數據呢?答案是:useReducer
。redux
好,有了思路,咱們來實現一下。後端
假設咱們在某一個層級有個須要共享狀態的父級元素,咱們稱它爲 Parent,在 Parent 下面不一樣層級之間有兩個 Child。這裏爲了簡單舉例假設兩個 Child 內都是共同的邏輯。異步
import React from "react"
function Parent() {
const colors = ['red', 'blue']
return (
<>
<Child1 color={colors[0]} />
<Child2 color={colors[1]} />
</>
)
}
function Child1(props) {
return (
<div style={{ background: props.color }}>I am {props.color}</div>
)
}
function Child2(props) {
return (
<div style={{ background: props.color }}>I am {props.color}</div>
)
}
複製代碼
咱們如今已經構造出了這樣的一個上下級結構,目前經過給子組件傳遞屬性,能夠實現父組件的狀態共享。可是這裏若是層級加深,咱們傳遞屬性的層級也要跟着加深。這樣顯然不是咱們想要的。
如今咱們來引入 Context
。
首先經過 createContext
方法初始化咱們須要的 Context
。
import React, { createContext } from "react"
const Context = createContext({
colors: ['red', 'blue']
})
複製代碼
而後咱們在 Parent 和 Child 裏引入剛纔的 Context,而且使用 useContext
拿到共享的數據:
import React, { useContext, createContext } from "react"
const Context = createContext({
colors: []
})
function Parent() {
const initState = {
colors: ["red", "blue"]
}
return (
<Context.Provider value={{ colors: initState.colors }}> <> {/* 僞裝這些地方有着不一樣的層級 */} <Child1 /> <Child2 /> </> </Context.Provider> ) } function Child1(props) { const { colors } = useContext(Context); return ( <div style={{ background: colors[0] }}> I am {colors[0]} </div> ) } // 省略 Child2 代碼,同 Child1 一致 複製代碼
如今只是拿到了數據而且進行渲染,再進一步,經過點擊元素,修改顏色。在這裏咱們就須要用 useReducer
來模擬觸發改變。
首先咱們須要一個 reducer 來處理觸發的改變。
function reducer(state, action) {
const { colors } = action
if (action.type === "CHANGE_COLOR") {
return { colors: colors }
} else {
throw new Error()
}
}
複製代碼
這裏我簡化了 action 的處理,固然你也能夠進行擴展。
如今,咱們給 Provider
加上提供改變的方法 dispatch。
import React, { useContext, createContext } from "react"
const Context = createContext({
colors: []
})
function Parent() {
const initState = {
colors: ["red", "blue"]
}
const [state, dispatch] = useReducer(reducer, initState)
return (
<Context.Provider value={{ colors: state.colors, dispatch: dispatch }}> <> {/* 僞裝這些地方有着不一樣的層級 */} <Child1 /> <Child2 /> </> </Context.Provider> ) } 複製代碼
而後子組件觸發改變:
function Child1(props) {
const { colors, dispatch } = useContext(Context)
return (
<div style={{ background: colors[0] }} onClick={() => dispatch({ type: "CHANGE_COLOR", colors: ["yellow", "blue"] }) } > I am {colors[0]} </div>
)
}
// 省略 Child2 代碼,同 Child1 一致
複製代碼
至此,這個小型的狀態共享便完成了。這即是咱們擺脫 Redux
以後實現的狀態共享思路的雛形。完整的代碼及例子見 tiny redux。
在實際的應用中,咱們的業務場景會更復雜,好比咱們的數據是動態獲取的。
這種狀況下你能夠把 Provider
抽出來,當 Parent 數據回來以後再初始化 Context。
function Provider (props) {
const { colors } = props
const initState = {
colors,
}
const [state, dispatch] = useReducer(reducer, initState)
return (
<Context.Provider value={{ colors: state.colors, dispatch: dispatch }}> {props.children} </Context.Provider> ) } 複製代碼
而後咱們在 Parent 中作異步操做,並把動態數據傳給 Provider :
import React, { useState, useEffect } from "react"
function Parent (props) {
const [data, setData] = useState()
const [url, setUrl] = useState('https://example.com')
useEffect(() => {
fetch(url).then(res => setData(data))
}, [url])
if (!data) return <div>Loading ...</div>
return (
<Provider colors={data}> <> {/* 僞裝這些地方有着不一樣的層級 */} <Child1 /> <Child2 /> </> </Provider>
)
}
複製代碼
這樣小型的狀態管理機制你甚至能夠放在某個組件裏,而不用放到如 Redux
全局的環境中去。這樣使得咱們寫的應用更加靈活,而不是一味的往 store
裏丟狀態。固然你也能夠寫一個 AppProvider 來管理全局的狀態,React Hooks
+ Context
給了咱們這樣的便利。
Hooks 真香!