react的狀態管理

近兩年前端技術的發展如火如荼,大量的前端項目都在使用或轉向 Vue 和 React 的陣營, 由前端渲染頁面的單頁應用佔比也愈來愈高,這就表明前端工做的複雜度也在直線上升,前端頁面上展現的信息愈來愈多也愈來愈複雜。咱們知道,任何狀態都須要進行管理,那麼今天咱們來聊聊前端狀態管理。
前端狀態管理第三方出名的庫有: Flux、Redux、Vuex、Mobx 等
這裏專講react的狀態管理演變
javascript

redux

開發者門接觸最多的應該就是redux,這裏從淺入深的來逐步學習吧前端

1 .單純的使用純redux庫,參考redux1java

// 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;
    }
}

// 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種方式react

2 .使用redux + react-redux方式, 參考redux2redux

// 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, 源代碼在redux3ide

// 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)你得本身手動建立並關聯,因此便有了以下辦法。this

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了,看你喜歡哪一個了
        )
    }
};

這只是狀態管理最基本的用法,還有特殊狀況 好比異步action等等,沒有專門講,感興趣的能夠去看看,不過建議先看最普通和基礎的

相關文章
相關標籤/搜索