一句话归纳就是使用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
后的确少了很多重复的操做,但愿能分享给你们。记得给我点个赞喔,算是对我一种鼓励吧,哈哈!