zoukankan      html  css  js  c++  java
  • react的状态管理

    近两年前端技术的发展如火如荼,大量的前端项目都在使用或转向 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等等,没有专门讲,感兴趣的可以去看看,不过建议先看最普通和基础的

  • 相关阅读:
    SQL Server 索引结构及其使用(四)
    正确配置和使用SQL mail
    2进制、8进制、10进制、16进制...各种进制间的轻松转换(c#)
    配置远程服务器
    SQLServer基本函数
    将人民币的数字表示转化成大写表示(C#版)
    SQL Serer 索引全攻略
    亿众国际点对点文件传输程序
    表的相关操作
    windows文件副檔名說明
  • 原文地址:https://www.cnblogs.com/dshvv/p/11038690.html
Copyright © 2011-2022 走看看