近兩年前端技術的發展如火如荼,大量的前端項目都在使用或轉向 Vue 和 React 的陣營, 由前端渲染頁面的單頁應用佔比也愈來愈高,這就表明前端工做的複雜度也在直線上升,前端頁面上展現的信息愈來愈多也愈來愈複雜。咱們知道,任何狀態都須要進行管理,那麼今天咱們來聊聊前端狀態管理。
前端狀態管理第三方出名的庫有: Flux、Redux、Vuex、Mobx 等
這裏專講react的狀態管理演變javascript
開發者門接觸最多的應該就是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,這個學過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官方最推薦的使用方式,也是本文一路想引伸的,若是想單獨看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等等,沒有專門講,感興趣的能夠去看看,不過建議先看最普通和基礎的