react用什么管理状态
时间:2022-03-22 16:12
react管理状态的工具:1、利用hooks进行状态管理;2、利用Redux进行状态管理,这种方式的配套工具比较齐全,可以自定义各种中间件;3、利用Mobx进行状态管理,它通过透明的函数响应式编程使得状态管理变得简单和可扩展。 本教程操作环境:Windows7系统、react17.0.1版、Dell G3电脑。 jQuery 时代,JS 代码中混杂 DOM 结构,各个流程庞杂交织时,就形成面条式代码,当使用发布订阅模型时,调试会一团乱麻。 jQuery 是针对 "过程" 的命令式编程,而那么多命令,最终都是为了更新 UI 中的 "数据",为什么不直接去改数据呢? 北京 → 上海,把 city="北京" 变为 city="上海" 就行。不管飞机火车步行抛锚,也不管路上会不会遇到王宝强, 现代前端框架的意义,就是问题解决思路的革新,把对 "过程" 的各种命令,变为了对 "状态" 的描述。 什么是状态?状态就是 UI 中的动态数据。 2013 年 5 月 React 诞生。但 2015 年之前,大概都是 jQuery 的天下。2015 年 3 月 React 0.13.0 发布,带来了 class 组件写法。 在 React class 组件时代,状态就是 this.state,使用 this.setState 更新。 为避免一团乱麻,React 引入了 "组件" 和 "单向数据流" 的理念。有了状态与组件,自然就有了状态在组件间的传递,一般称为 "通信"。 父子通信较简单,而深层级、远距离组件的通信,则依赖于 "状态提升" + props 层层传递。 于是,React 引入了 Context,一个用于解决组件 "跨级" 通信的官方方案。 但 Context 其实相当于 "状态提升",并没有额外的性能优化,且写起来比较啰嗦。 为优化性能,一般会添加多个 Context,写起来就更啰嗦。在项目没那么复杂时,还不如层层传递简单。 实用主义来说,"状态管理" 就是为了解决组件间的 "跨级" 通信。 当然,在使用状态管理库时,其会带来一些衍生的思维模式,比如如何组织 state,如何拆分公共逻辑、业务逻辑、组件逻辑等,但归根结底,这些都不是核心缘由。 核心就是为了解决实际问题 —— 为了通信。其它的各种概念与哲学,都不是必要的。 Context 没那么好用,React 官方也没什么最佳实践,于是一个个社区库就诞生了。 目前比较常用的状态管理方式有hooks、redux、mobx三种,下面我将详细介绍一下这三类的使用方法以及分析各自的优缺点,以供各位进行参考。 用hooks进行状态管理主要有两种方式: useContext+useReducer 使用方法 1.创建store和reducer以及全局context src/store/reducer.ts 2.根组件通过 Provider 注入 context src/App.tsx 3.在组件中使用 src/components/Count/index.tsx src/components/Name/index.tsx useState+useEffect 使用方法 1.创建state和reducer src/global-states.ts src/global-states.type.ts 2.写一个发布订阅模式,让组件订阅globalState src/global-states.ts 3.组件中使用 src/App.tsx src/components/Count/index.tsx src/components/Name/index.tsx 优缺点分析 由于以上两种都是采用hooks进行状态管理,这里统一进行分析 优点 缺点 使用方法: 1.引入redux 2.新建reducer 在src/store/reducers文件夹下新建addReducer.ts(可建立多个reducer) 在src/stores文件夹下新建action.types.ts 3.合并reducer 在src/store/reducers文件夹下新建index.ts 3.创建store 在src/stores文件夹下新建index.ts 4.根组件通过 Provider 注入 store src/index.tsx(用provider将App.tsx包起来) 5.在组件中使用 src/somponents/Count/index.tsx src/somponents/Name/index.tsx 优缺点分析 优点 缺点 MobX 是一个经过战火洗礼的库,它通过透明的函数响应式编程(transparently applying functional reactive programming - TFRP)使得状态管理变得简单和可扩展。 常规使用(mobx-react) 使用方法 1.引入mobx 2.创建store 在/src/store目录下创建你要用到的store(在这里使用多个store进行演示) store2.ts 3.导出store src/store/index.ts 4.根组件通过 Provider 注入 store src/index.tsx(用provider将App.tsx包起来) 5.在组件中使用 src/somponents/Count/index.tsx src/components/Name/index.tsx 优缺点分析: 优点: 缺点: 最佳实践(mobx+hooks) 使用方法 1.引入mobx 同上 2.创建store 同上 3.导出store(结合useContext) src/store/index.ts 4.在组件中使用 无需使用Provider注入根组件 src/components/Name/index.tsx Mobx自动订阅实现原理 基本概念 建立依赖 我们给组件包的一层observer实现了这个功能 组件每次mount和update时都会执行一遍useObserver函数,useObserver函数中通过reaction.track进行依赖收集,将该组件加到该Observable变量的依赖中(bindDependencies)。 reaction.track() reaction.track里面的核心内容是trackDerivedFunction 触发依赖 Observable(被观察者,状态)修改后,会调用它的set方法,然后再依次执行该Observable之前收集的依赖函数,触发rerender。 组件更新 用组件更新来简单阐述总结一下:mobx的执行原理。 observer这个装饰器(也可以是Hoc),对React组件的render方法进行track。 将render方法,加入到各个observable的依赖中。当observable发生变化,track方法就会执行。 track中,还是先进行依赖收集,调用forceUpdate去更新组件,然后结束依赖收集。 每次都进行依赖收集的原因是,每次执行依赖可能会发生变化。 简单总结了一下目前较为常用的状态管理方式,我个人最喜欢的使用方式是Mobx+Hooks,简单轻量易上手。各位可以根据自己的需求选择适合自己项目的管理方式。 【相关推荐:Redis视频教程】 以上就是react用什么管理状态的详细内容,更多请关注gxlsystem.com其它相关文章!什么是 "状态"?
React 中的状态
什么是 "状态管理"?
react中的状态管理方式
Hooks状态管理
import React from "react";
// 初始状态
export const state = {
count: 0,
name: "ry",
};
// reducer 用于修改状态
export const reducer = (state, action) => {
const { type, payload } = action;
switch (type) {
case "ModifyCount":
return {
...state,
count: payload,
};
case "ModifyName":
return {
...state,
name: payload,
};
default: {
return state;
}
}
};
export const GlobalContext = React.createContext(null);
import React, { useReducer } from "react";
import './index.less'
import { state as initState, reducer, GlobalContext} from './store/reducer'
import Count from './components/Count'
import Name from './components/Name'
export default function () {
const [state, dispatch] = useReducer(reducer, initState);
return (
<div>
<GlobalContext.Provider value={{state, dispatch}}>
<Count />
<Name />
</GlobalContext.Provider>
</div>
)
}
import { GlobalContext } from "@/store/reducer";
import React, { FC, useContext } from "react";
const Count: FC = () => {
const ctx = useContext(GlobalContext)
return (
<div>
<p>count:{ctx.state.count}</p>
<button onClick={() => ctx.dispatch({ type: "ModifyCount", payload: ctx.state.count+1 })}>+1</button>
</div>
);
};
export default Count;
import { GlobalContext } from "@/store/reducer";
import React, { FC, useContext } from "react";
const Name: FC = () => {
const ctx = useContext(GlobalContext)
console.log("NameRerendered")
return (
<div>
<p>name:{ctx.state.name}</p>
</div>
);
};
export default Name;
// 初始state
let globalState: GlobalStates = {
count: 0,
name: 'ry'
}
// reducer
export const modifyGlobalStates = (
operation: GlobalStatesModificationType,
payload: any
) => {
switch (operation) {
case GlobalStatesModificationType.MODIFY_COUNT:
globalState = Object.assign({}, globalState, { count: payload })
break
case GlobalStatesModificationType.MODIFY_NAME:
globalState = Object.assign({}, globalState, { name: payload })
break
}
broadcast()
}
export interface GlobalStates {
count: number;
name: string;
}
export enum GlobalStatesModificationType {
MODIFY_COUNT,
MODIFY_NAME
}
import { useState, useEffect } from 'react'
import {
GlobalStates,
GlobalStatesModificationType
} from './global-states.type'
let listeners = []
let globalState: GlobalStates = {
count: 0,
name: 'ry'
}
// 发布,所有订阅者收到消息,执行setState重新渲染
const broadcast = () => {
listeners.forEach((listener) => {
listener(globalState)
})
}
export const modifyGlobalStates = (
operation: GlobalStatesModificationType,
payload: any
) => {
switch (operation) {
case GlobalStatesModificationType.MODIFY_COUNT:
globalState = Object.assign({}, globalState, { count: payload })
break
case GlobalStatesModificationType.MODIFY_NAME:
globalState = Object.assign({}, globalState, { name: payload })
break
}
// 状态改变即发布
broadcast()
}
// useEffect + useState实现发布订阅
export const useGlobalStates = () => {
const [value, newListener] = useState(globalState)
useEffect(() => {
// newListener是新的订阅者
listeners.push(newListener)
// 组件卸载取消订阅
return () => {
listeners = listeners.filter((listener) => listener !== newListener)
}
})
return value
}
import React from 'react'
import './index.less'
import Count from './components/Count'
import Name from './components/Name'
export default function () {
return (
<div>
<Count />
<Name />
</div>
)
}
import React, { FC } from 'react'
import { useGlobalStates, modifyGlobalStates } from '@/store/global-states'
import { GlobalStatesModificationType } from '@/store/global-states.type'
const Count: FC = () => {
// 调用useGlobalStates()即订阅globalStates()
const { count } = useGlobalStates()
return (
<div>
<p>count:{count}</p>
<button
onClick={() =>
modifyGlobalStates(
GlobalStatesModificationType.MODIFY_COUNT,
count + 1
)
}
>
+1
</button>
</div>
)
}
export default Count
import React, { FC } from 'react'
import { useGlobalStates } from '@/store/global-states'
const Count: FC = () => {
const { name } = useGlobalStates()
console.log('NameRerendered')
return (
<div>
<p>name:{name}</p>
</div>
)
}
export default Count
Redux状态管理
yarn add redux react-redux @types/react-redux redux-thunk
import * as types from '../action.types'
import { AnyAction } from 'redux'
// 定义参数接口
export interface AddState {
count: number
name: string
}
// 初始化state
let initialState: AddState = {
count: 0,
name: 'ry'
}
// 返回一个reducer
export default (state: AddState = initialState, action: AnyAction): AddState => {
switch (action.type) {
case types.ADD:
return { ...state, count: state.count + action.payload }
default:
return state
}
}
主要用于声明action类型export const ADD = 'ADD'
export const DELETE = 'DELETE'
import { combineReducers, ReducersMapObject, AnyAction, Reducer } from 'redux'
import addReducer, { AddState } from './addReducer'
// 如有多个reducer则合并reducers,模块化
export interface CombinedState {
addReducer: AddState
}
const reducers: ReducersMapObject<CombinedState, AnyAction> = {
addReducer
}
const reducer: Reducer<CombinedState, AnyAction> = combineReducers(reducers)
export default reducer
import {
createStore,
applyMiddleware,
StoreEnhancer,
StoreEnhancerStoreCreator,
Store
} from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducers'
// 生成store增强器
const storeEnhancer: StoreEnhancer = applyMiddleware(thunk)
const storeEnhancerStoreCreator: StoreEnhancerStoreCreator = storeEnhancer(createStore)
const store: Store = storeEnhancerStoreCreator(reducer)
export default store
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
import React, { FC } from 'react'
import { connect } from 'react-redux'
import { Dispatch } from 'redux'
import { AddState } from 'src/store/reducers/addReducer'
import { CombinedState } from 'src/store/reducers'
import * as types from '@/store/action.types'
// 声明参数接口
interface Props {
count: number
add: (num: number) => void
}
// ReturnType获取函数返回值类型,&交叉类型(用于多类型合并)
// type Props = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>
const Count: FC<Props> = (props) => {
const { count, add } = props
return (
<div>
<p>count: {count}</p>
<button onClick={() => add(5)}>addCount</button>
</div>
)
}
// 这里相当于自己手动做了映射,只有这里映射到的属性变化,组件才会rerender
const mapStateToProps = (state: CombinedState) => ({
count: state.addReducer.count
})
const mapDispatchToProps = (dispatch: Dispatch) => {
return {
add(num: number = 1) {
// payload为参数
dispatch({ type: types.ADD, payload: num })
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Count)
import React, { FC } from 'react'
import { connect } from 'react-redux'
import { Dispatch } from 'redux'
import { AddState } from 'src/store/reducers/addReducer'
import { CombinedState } from 'src/store/reducers'
import * as types from '@/store/action.types'
// 声明参数接口
interface Props {
name: string
}
const Name: FC<Props> = (props) => {
const { name } = props
console.log('NameRerendered')
return (
<div>
<p>name: {name}</p>
</div>
)
}
// name变化组件才会rerender
const mapStateToProps = (state: CombinedState) => ({
name: state.addReducer.name
})
// addReducer内任意属性变化组件都会rerender
// const mapStateToProps = (state: CombinedState) => state.addReducer
export default connect(mapStateToProps)(Name)
Mobx状态管理
yarn add mobx mobx-react -D
例如:
store1.tsimport { observable, action, makeObservable } from 'mobx'
class Store1 {
constructor() {
makeObservable(this) //mobx6.0之后必须要加上这一句
}
@observable
count = 0
@observable
name = 'ry'
@action
addCount = () => {
this.count += 1
}
}
const store1 = new Store1()
export default store1
这里使用 makeAutoObservable代替了makeObservable,这样就不用对每个state和action进行修饰了(两个方法都可,自行选择)import { makeAutoObservable } from 'mobx'
class Store2 {
constructor() {
// mobx6.0之后必须要加上这一句
makeAutoObservable(this)
}
time = 11111111110
}
const store2 = new Store2()
export default store2
import store1 from './store1'
import store2 from './store2'
export const store = { store1, store2 }
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './store'
import { Provider } from 'mobx-react'
ReactDOM.render(
<Provider {...store}>
<App />
</Provider>,
document.getElementById('root')
)
import React, { FC } from 'react'
import { observer, inject } from 'mobx-react'
// 类组件用装饰器注入,方法如下
// @inject('store1')
// @observer
interface Props {
store1?: any
}
const Count: FC<Props> = (props) => {
const { count, addCount } = props.store1
return (
<div>
<p>count: {count}</p>
<button onClick={addCount}>addCount</button>
</div>
)
}
// 函数组件用Hoc,方法如下(本文统一使用函数组件)
export default inject('store1')(observer(Count))
import React, { FC } from 'react'
import { observer, inject } from 'mobx-react'
interface Props {
store1?: any
}
const Name: FC<Props> = (props) => {
const { name } = props.store1
console.log('NameRerendered')
return (
<div>
<p>name: {name}</p>
</div>
)
}
// 函数组件用Hoc,方法如下(本文统一使用函数组件)
export default inject('store1')(observer(Name))
import React from 'react'
import store1 from './store1'
import store2 from './store2'
// 导出store1
export const storeContext1 = React.createContext(store1)
export const useStore1 = () => React.useContext(storeContext1)
// 导出store2
export const storeContext2 = React.createContext(store2)
export const useStore2 = () => React.useContext(storeContext2)
src/somponents/Count/index.tsximport React, { FC } from 'react'
import { observer } from 'mobx-react'
import { useStore1 } from '@/store/'
// 类组件可用装饰器,方法如下
// @observer
const Count: FC = () => {
const { count, addCount } = useStore1()
return (
<div>
<p>count: {count}</p>
<button onClick={addCount}>addCount</button>
</div>
)
}
// 函数组件用Hoc,方法如下(本文统一使用函数组件)
export default observer(Count)
import React, { FC } from 'react'
import { observer } from 'mobx-react'
import { useStore1 } from '@/store/'
const Name: FC = () => {
const { name } = useStore1()
console.log('NameRerendered')
return (
<div>
<p>name: {name}</p>
</div>
)
}
export default observer(Name)
优缺点分析:
优点:
缺点:
Observable //被观察者,状态
Observer //观察者,组件
Reaction //响应,是一类的特殊的 Derivation,可以注册响应函数,使之在条件满足时自动执行。
export default observer(Name)
// fn = function () { return baseComponent(props, ref);
export function useObserver(fn, baseComponentName) {
...
var rendering;
var exception;
reaction.track(function () {
try {
rendering = fn();
}
catch (e) {
exception = e;
}
});
if (exception) {
throw exception; // re-throw any exceptions caught during rendering
}
return rendering;
}
_proto.track = function track(fn) {
// 开始收集
startBatch();
var result = trackDerivedFunction(this, fn, undefined);
// 结束收集
endBatch();
};
function trackDerivedFunction<T>(derivation: IDerivation, f: () => T, context: any) {
...
let result
// 执行回调f,触发了变量(即组件的参数)的 get,从而获取 dep【收集依赖】
if (globalState.disableErrorBoundaries === true) {
result = f.call(context)
} else {
try {
result = f.call(context)
} catch (e) {
result = new CaughtException(e)
}
}
globalState.trackingDerivation = prevTracking
// 给 observable 绑定 derivation
bindDependencies(derivation)
...
return result
}
总结