一句話歸納就是使用Redux
太麻煩了:css
Redux
狀態樹變多時,維護更加困難自從React
發佈了React Hook
後,你們就想到了使用useContext
和useReducer
來模擬Redux
html
好處是什麼:react
Redux
分離使用,只需在用到的地方添加狀態,至關於一個局部的全局狀態。React
時開發先分別介紹一下useContext
和useReducer
分別是作什麼的:git
要使用useContext
以前,咱們須要先知道Context
是什麼,官方文檔裏面有說明github
Context 設計目的是爲了共享那些對於一個組件樹而言是「全局」的數據,例如當前認證的用戶、主題或首選語言。typescript
也就是說,使用Context
來管理咱們的「全局」狀態是再好不過的了。bash
而useContext
就是爲了接收context
對象並返回context
的值而存在的。說白了就是咱們能夠經過useContext
來訪問全局狀態Context
。app
咱們先來看一下useReducer
的函數。ide
const [state, dispatch] = useReducer(reducer, initialArg, init);
複製代碼
它接收一個如(state, action) => newState
的reducer
,並返回當前的state
以及預期配套的dispath
方法。(用過Redux
應該不會陌生)官方文檔函數
下面咱們用一個簡單的例子經過useContext
和useReducer
來實現Redux
的效果。
該例子使用實現主題色切換來管理全局狀態
你能夠經過create-react-app
建立一個React
項目進行測試,也能夠在CodeSandbox進行模擬
筆者用的是TypeScript
進行項目編寫的,你也能夠經過
npx create-react-app my-app(項目名) --typescript
# 或者
yarn create react-app my-app(項目名) --typescript
複製代碼
建立一個TypeScript
的React
項目
建立好項目後,在src
目錄下新建一個pages
pages
內分別建立switch
和theme
兩個組件,這裏只列出主要目錄結構
.
├── src
| └── pages
| | ├── switch
| | | └── index.tsx // 用來修改theme狀態
| | └── state
| | | └── index.tsx // 用來顯示theme狀態
└── store
└── theme.tsx // 用來存儲theme狀態
複製代碼
index.tsx
import React from 'react'
const State: React.FC = () => {
return (
<div>由我顯示全局變量</div>
)
}
export default State;
複製代碼
index.tsx
import React from 'react'
const Switch: React.FC = () => {
return (
<section>
<button>我是改變狀態1</button>
<button>我是改變狀態2</button>
</section>
)
}
export default Switch;
複製代碼
如今咱們把這兩個組件都引入到App.tsx
內
import React from 'react';
import Switch from './pages/switch'
import State from './pages/state'
import './App.css';
const App: React.FC = () => {
return (
<div className="App">
<Switch></Switch>
<State></State>
</div>
);
}
export default App;
複製代碼
此時咱們運行咱們的項目,能夠看到頁面大概是這樣的
此時咱們第一步就算完成了
在src
目錄下建立一個store
目錄文件,新建一個文件,名爲theme.tsx
theme.tsx
初始化主題值
import React, { createContext, Context } from 'react'
// 定義主題色的接口
interface ITheme {
theme: string
}
// 初始化
export const initialTheme: ITheme = {
theme: '等待改變主題'
}
// 建立一個Context實例
export const ThemeContext: Context<any> = createContext(initialTheme);
/**
* 建立一個 Theme 組件
* Theme 組件包裹的全部子組件均可以經過調用 ThemeContext 訪問到 value
*/
export const Theme: React.FC = (props) => {
return (
<ThemeContext.Provider value={{state: initialTheme}}>
{props.children}
</ThemeContext.Provider>
)
}
複製代碼
這裏有個重點就是,當ThemeContext.Provider
的value
值發生變化時,它內部的全部子組件都會從新渲染。
因此咱們須要將Theme
組件包裹在State
和Switch
的外層
此時App.tsx變爲
import React from 'react';
import Switch from './pages/switch'
import State from './pages/state'
import { Theme } from './store/theme'
import './App.css';
const App: React.FC = () => {
return (
<div className="App">
<Theme>
<Switch></Switch>
<State></State>
</Theme>
</div>
);
}
export default App;
複製代碼
此時咱們第二步就算完成了
如今咱們能夠在State
組件內經過useContext
來讀取Theme
的狀態了
// 引入useContext
import React, { useContext } from 'react'
// 引入Theme的Context
import { ThemeContext } from '../../store/theme'
const State: React.FC = () => {
// 獲取Theme傳來的value值
const { state } = useContext(ThemeContext)
return (
<div className="theme light">{state.theme}</div>
)
}
export default State;
複製代碼
此時運行咱們的項目,發現State
已經讀取到了Theme
的值,說明咱們的第三步已經成功了
接下來咱們須要經過Switch
內的兩個按鈕來改變Theme
的值,看下State
組件內的狀態有沒發生變化
在Theme
組件內使用useReducer
而且添加reducer方法用於修改theme
的狀態
import React, { createContext, Context, useReducer } from 'react'
// 定義主題色的接口
interface ITheme {
theme: string
}
// 初始化
export const initialTheme: ITheme = {
theme: '等待改變主題'
}
// 建立一個Context實例
export const ThemeContext: Context<any> = createContext(initialTheme);
// (新增)初始化store的類型、初始化值、reducer
export const CHANGE_THEME: string = 'CHANGE_THEME';
// (新增)編寫reducer函數
export const reducer = (state: ITheme, action: any) => {
switch (action.type) {
case CHANGE_THEME:
return { ...state, theme: action.theme }
default:
throw new Error();
}
}
/**
* 建立一個 Theme 組件
* Theme 組件包裹的全部子組件均可以經過調用 ThemeContext 訪問到 value
*/
export const Theme: React.FC = (props) => {
// 經過使用useReducer更新狀態
const [state, dispatch] = useReducer(reducer, initialTheme);
return (
<ThemeContext.Provider value={{state, dispatch}}>
{props.children}
</ThemeContext.Provider>
)
}
複製代碼
此時咱們的Theme
組件已所有編寫完畢。
咱們Switch
也經過useContext
來讀取Theme
的狀態,而後在button
內添加修改狀態的dispatch
,即可以修改Theme
的狀態了
修改後的Swtich
組件
import React, { useContext } from 'react'
import { ThemeContext, CHANGE_THEME } from '../../store/theme'
const Switch: React.FC = () => {
// 調用dispatch修改狀態
const { dispatch } = useContext(ThemeContext)
return (
<section>
<button
onClick={() => {
dispatch({ type: CHANGE_THEME, theme: "Change One" });
}}>
我是改變狀態1
</button>
<button
onClick={() => {
dispatch({ type: CHANGE_THEME, theme: "Change Two" });
}}>
我是改變狀態2
</button>
</section>
)
}
export default Switch;
複製代碼
運行項目效果以下
至此,全部步驟完成。
demo已傳到了GitHub,能夠和你的代碼進行對比😜
寫這篇文章的初衷就是以爲在React
中使用一次Redux
真的太麻煩了,有了React Hook
後的確少了很多重複的操做,但願能分享給你們。記得給我點個贊喔,算是對我一種鼓勵吧,哈哈!