近两年前端技术的发展如火如荼,大量的前端项目都在使用或转向 Vue 和 React 的阵营, 由前端渲染页面的单页应用占比也越来越高,这就代表前端工作的复杂度也在直线上升,前端页面上展示的信息越来越多也越来越复杂。我们知道,任何状态都需要进行管理,那么今天我们来聊聊前端状态管理。

前端状态管理第三方出名的库有: Flux、Redux、Vuex、Mobx 等

这里专讲react的状态管理演变

redux

开发者门接触最多的应该就是redux,这里从浅入深的来逐步学习吧

1 .单纯的使用纯redux库,参考redux1

// Action
export const commonType = {//action type
SET_AGE: 'SET_AGE'
}
export function setAge(payload: any) {//action creator
return {
type: commonType.SET_AGE,
payload
}
} // reducer
const commonReducer = (state = initialState, action: any) =>{
switch (action.type) {
case commonType.SET_AGE:
return { ...state, ...action.payload };
default:
return state;
}
}
const allReducer = combineReducers({
common:commonReducer
}) // store
import { createStore } from 'redux'
let store = createStore(allReducer);
// 使用(订阅)
import store from '../redux/store'
export default class App extends React.Component<any, any> {
constructor(props: any) {
super(props);
this.state = {iptVal:0};
}
componentDidMount() {
store.subscribe(() =>{
let {common:{age:iptVal}}= store.getState();
this.setState({iptVal});
})
}
render() {
return (<div>{this.state.iptVal}</div>);
}
}
// 使用(广播)
import store from '../redux/store'
export default class A extends React.Component<any, any> {
constructor(props: any) {
super(props);
this.state = {iptVal:0};
}
componentDidMount() {
store.subscribe(() =>{
let {common:{age:iptVal}}= store.getState();
this.setState({iptVal});
})
}
iptChange = (e: any) =>{
store.dispatch(actions.setAge({
age:e.target.value
}));
}
render() {
const { iptVal } = this.state;
return (
<>
<input type="text" value={iptVal} onChange={this.iptChange} />
<div>{iptVal}</div>
</>
)
}
}

缺点很明显,需要在改变和监听数据的地方都引入store,并手动与组件关联,因此有了第2种方式

2 .使用redux + react-redux方式, 参考redux2

// Action
export const commonType = {//action type
SET_AGE: 'SET_AGE'
}
export function setAge(payload: any) {//action creator
return {
type: commonType.SET_AGE,
payload
}
} // reducer
import { combineReducers } from 'redux';
const commonReducer = (state = initialState, action: any) =>{
switch (action.type) {
case commonType.SET_AGE:
return { ...state, ...action.payload };
default:
return state;
}
}
const allReducer = combineReducers({
common:commonReducer
}) // store
import { createStore } from 'redux'
let store = createStore(allReducer);
// 使用(订阅)
import { connect } from 'react-redux'
class App extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
render() {
return (<div>{this.props.iptVal}</div>);
}
}
export default connect(
(state: any) => {
return {iptVal: state.common.age}
},null
)(App);
// 使用(广播)
import { connect } from 'react-redux'
class A extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
render() {
return (
<>
<input type="text" value={this.props.iptVal} onChange={this.props.iptChange} />
<div>{this.props.iptVal}</div>
</>
)
}
}
export default connect(
(state: any) => {
return {iptVal: state.common.age}
},
(dispatch: any)=>{return {
iptChange(e: any){
dispatch(actions.setAge({
age:e.target.value
}))
}
}}
)(A);

这样就不用手动处理全局状态与react的关系了,如果你了解注解(装饰器),看起来代码就更简单了,反正我是没有配置成功,你可以试试

不过action creator和reducer创建起来好费劲。action creator要写大量的重复代码,reducer遍地的switch case,所以便有了第3种方式。

3 .redux + react-redux + redux-actions, 源代码在redux3

// Action
import { createAction } from 'redux-actions';
export const commonType = {SET_AGE: 'SET_AGE'};//action type
export const setAge = createAction(commonType.SET_AGE);//action creator // reducer
import { combineReducers } from 'redux';
import { handleActions } from "redux-actions";
const initialState = {age: 0}; //初始化state
let reducers = {
[commonType.SET_AGE](state: any, action: any){
let { payload } = action;
return {...state,...payload};
}
};
const commonReducer = handleActions<any>(reducers,initialState);
const allReducer = combineReducers({
common:commonReducer
}) // store
import { createStore } from 'redux'
let store = createStore(allReducer);
// 使用(订阅)
import { connect } from 'react-redux'
class App extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
render() {
return (<div>{this.props.iptVal}</div>);
}
}
export default connect(
(state: any) => {
return {iptVal: state.common.age}
},null
)(App);
// 使用(广播)
import { connect } from 'react-redux'
class A extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
render() {
let {iptVal,iptChange} = this.props;
return (<>
<input type="text" value={iptVal} onChange={iptChange}/>
<div>{iptVal}</div>
</>)
}
}
export default connect(
(state: any) => {
return {iptVal: state.common.age}
},
(dispatch: any)=>{return {
iptChange(e: any){
dispatch(actions.setAge({
age:e.target.value
}))
}
}}
)(A);

这样做效果已经很好了,至少在hooks来之前,这是大家普遍使用的方法来管理react的全局状态。但是hooks之后,我推荐如下,原因是 不用引入任何第三方包

React.Context

使用作用域之React.Context,这个学过java的人都知道,此对象是贯穿整个应用的。通过注入便监听 Context来达到redux同样的效果,好不用引入第三方包。参考context

// Context
const commonContext = React.createContext({ //初始化,不具体实现
age: 0,
setAge: (age: number) => {}
}); // 使用(注入需要订阅的组件)
import {useState} from 'react';
import CommonContext from './context';
export default const App = () => {
const [ age, setAges ] = useState(10);
let myValue = {
age,setAge(age: number){setAges(age)}
}; return (<>
<CommonContext.Provider value={myValue}>
<A/>
<B/>
<C/>
<div>{age}</div>
</CommonContext.Provider>
</>);
}
// 使用(发起广播)
import * as React from 'react';
import { useContext } from 'react';
import commonContext from '../context'; export default const B =()=> {
const commonCtx = useContext(commonContext);
const onChange = (e: any)=>{
commonCtx.setAge(e.target.value)
};
return (<>
<div>{commonCtx.age}</div>
<input type="text" onChange={onChange} value={commonCtx.age}/>
</>)
}
// 使用(订阅监听)--函数式组件使用hooks订阅
import * as React from 'react';
import { useContext } from 'react';
import commonContext from '../context'; export default const A = (props: any) => {
const commonCtx = useContext(commonContext);
return (<div>{commonCtx.age}</div>)
}
// 使用(订阅监听)--类组件两种方式订阅
import * as React from 'react'
import commonContext from '../context'; export default class C extends React.Component <any,any> {
static contextType = commonContext;
constructor(props: any){
super(props);
}
render(){
return (
// 在没有useContext的hooks之前,通常这样取得和监听Context
<>
方式1:this.context,使用Class.contextType你可以在任何生命周期中访问到this.context:
<div>{this.context.age}</div>
方式2:Consumer, 让你在函数式组件中完成订阅 context:
<commonContext.Consumer>
{commonCtx=><div>{commonCtx.age}</div>}
</commonContext.Consumer>
</>
)
}
};

如果只是用Context,功能能实现,但是还不是很灵活,比如动态的value(state和reducer)你得自己手动创建并关联,所以便有了如下办法。

React.Context 和 hooks之useReducer

这是目前react官方最推荐的使用方式,也是本文一路想引申的,如果想单独看useReducer的使用方式请看useReducer,最终结合版看useReducerContext

// Context
const commonContext: any = React.createContext(null); //action
export const commonType = {
SET_AGE:'SET_AGE'
}
export const setAge = (payload: any) =>{
return {
type: commonType.SET_AGE,
payload
}
} //reducer
const initialState: any = {age: 0};
function reducer(state: any, action: any) {
switch (action.type) {
case commonType.SET_AGE:
let { payload } = action;
return {...state,...payload};
default:
throw new Error();
}
}
export {
initialState,
reducer
};
//使用(注入需要订阅的组件)
import * as React from 'react';
import A from './components/a';
import B from './components/b';
import C from './components/c';
import {useReducer} from 'react';
import { reducer, initialState } from './redux/reducer/common';
import CommonContext from './context'; export default () => {
const myValue = useReducer(reducer, initialState);
return (
<div>
<CommonContext.Provider value={myValue}>
<A/>
<B/>
<C/>
<div>{myValue[0].age}</div>
</CommonContext.Provider>
</div>
);
}
// 使用(发起广播)
import * as React from 'react';
import { useContext } from 'react';
import commonContext from '../context'; export default const B =()=> {
const [state,dispatch] = useContext(commonContext);
const onChange = (e: any)=>{
let payload = {
age: e.target.value
}
dispatch(setAge(payload))
};
return (<>
<div>{state.age}</div>
<input type="text" onChange={onChange} value={state.age}/>
</>)
}
// 使用(订阅监听)--函数式组件使用hooks订阅
import * as React from 'react';
import { useContext } from 'react';
import commonContext from '../context';
export default const A = (props: any) => {
const [state] = useContext(commonContext);
return (<div>{state.age}</div>)
}
// 使用(订阅监听)--类组件两种方式订阅
import * as React from 'react'
import commonContext from '../context'; export default class C extends React.Component <any,any> {
static contextType = commonContext;
constructor(props: any){
super(props);
}
render(){
return (
// 在没有useContext的hooks之前,通常这样取得和监听Context
<>
{/* 方式1:this.context,使用Class.contextType你可以在任何生命周期中访问到this.context */}
<div>{this.context[0].age}</div>
{/* 方式2:Consumer, 让你在函数式组件中完成订阅 context */}
<commonContext.Consumer>
{([state]:any)=>{
return <div>{state.age}</div>
}}
</commonContext.Consumer>
</>
//总结:使用useContext()时候我们可以不需要使用Consumer了,看你喜欢哪个了
)
}
};

demo地址:https://gitee.com/dshvv/reactStatus

这只是状态管理最基本的用法,还有特殊情况 比如异步action等等,没有专门讲,感兴趣的可以去看看,不过建议先看最普通和基础的

react的状态管理的更多相关文章

  1. React的状态管理工具

    Mobx-React : 当前最适合React的状态管理工具   MobX 简单.可扩展的状态管理        MobX 是由 Mendix.Coinbase.Facebook 开源和众多个人赞助商 ...

  2. react+redux状态管理实现排序 合并多个reducer文件

    这个demo只有一个reducer 所以合并reducer这个demo用不到 ,但是我写出来这样大家以后可以用到,很好用,管理多个reducer,因为只要用到redux就不会只有一个reducer所以 ...

  3. Mobx-React : 当前适合React的状态管理工具

    MobX 简单.可扩展的状态管理        MobX 是由 Mendix.Coinbase.Facebook 开源和众多个人赞助商所赞助的.    安装 安装: npm install mobx ...

  4. React + MobX 状态管理入门及实例

    前言 现在最热门的前端框架,毫无疑问是React. React是一个状态机,由开始的初始状态,通过与用户的互动,导致状态变化,从而重新渲染UI. 对于小型应用,引入状态管理库是"奢侈的&qu ...

  5. 对于React各种状态管理器的解读

    首先我们要先知道什么是状态管理器,这玩意是干啥的? 当我们在多个页面中使用到了相同的属性时就可以用到状态管理器,将这些状态存到外部的一个单独的文件中,不管在什么时候想使用都可以很方便的获取. reac ...

  6. 你再也不用使用 Redux、Mobx、Flux 等状态管理了

    Unstated Next readme 的中文翻译 前言 这个库的作者希望使用 React 内置 API ,直接实现状态管理的功能.看完这个库的说明后,没有想到代码可以这个玩.短短几行代码,仅仅使用 ...

  7. react框架的状态管理

    安装: cnpm install --save redux cnpm install --save react-redux   安装好后导入模块内容: impor {createStore} from ...

  8. React项目中使用Mobx状态管理(二)

    并上一节使用的是普通的数据状态管理,不过官方推荐使用装饰器模式,而在默认的react项目中是不支持装饰器的,需要手动启用. 官方参考 一.添加配置 官方提供了四种方法, 方法一.使用TypeScrip ...

  9. React项目中使用Mobx状态管理(一)

    1.安装 $ yarn add mobx mobx-react 2.新建store/index.js,存放数据(以下思路仅限于父子组件的简单应用) 注意:这里暂时没使用装饰器@observable,装 ...

随机推荐

  1. postgre-插入数据时的单引号问题

    场景: 将一个HTML页面存储到数据库中 问题: HTML页面中既包含单引号也包含双引号 解决办法: 双单引号 INSERT INTO table VALUES ('<html><s ...

  2. Go语言 之TCP聊天室

    服务端流程图如下: package main import ( "fmt" "net" ) // 客户端结构体 type Client struct { //用 ...

  3. Redis 的几种常见使用方式

    常见使用方式 Redis 的几种常见使用方式包括: Redis 单副本 Redis 多副本(主从) Redis Sentinel(哨兵) Redis Cluster Redis 自研 各种使用方式的优 ...

  4. Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析

    Java 读写锁 ReentrantReadWriteLock 源码分析 转自:https://www.javadoop.com/post/reentrant-read-write-lock#toc5 ...

  5. 客户端配置代理服务实现yum上外网

    vi  /etc/profile http_proxy=http://172.20.188.193:3128/https_proxy=https://172.20.188.193:3128/expor ...

  6. vue单页面项目架构方案

    这里的架构方案是基于vue-cli2生成的项目应用程序产生的,是对项目应用程序或者项目模板的一些方便开发和维护的封装.针对单页面的解决方案. 主要有四个方面: 一,不同环境下的分别打包 主要是测试环境 ...

  7. 生成要发送到社区的内核补丁时如何指定发布的版本号(v2,v3...)?

    1. 生成一个补丁 git format-patch --subject-prefix=v2 -1 那么生成的patch文件就会有如下类似的信息: Subject: [v2] your descrip ...

  8. kotlin中集合

    fun main(arg: Array<String>) { //可读写的集合创建 val mutableListOf1 = mutableListOf<Int>(1, 2, ...

  9. C# 去除所有的html标签

    /// <summary> /// 去除所有的html标签 /// </summary> /// <param name="strhtml">& ...

  10. RocketMQ采坑记

    先来一篇解释比较多的实例 https://www.cnblogs.com/super-d2/p/4154541.html No route info of this topic, PushTopic ...