原文地址: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 真香!