Outils de gestion d'état de React : 1. Utilisez des hooks pour la gestion de l'état ; 2. Utilisez Redux pour la gestion de l'état. Cette méthode dispose d'un ensemble relativement complet d'outils de support et peut personnaliser divers middlewares. 3. Utilisez Mobx pour la gestion de l'état. est rendu simple et évolutif grâce à une programmation réactive fonctionnelle transparente.
L'environnement d'exploitation de ce tutoriel : système Windows 7, React version 17.0.1, ordinateur Dell G3.
À l'ère de jQuery, les structures DOM sont mélangées dans le code JS, et lorsque divers processus sont compliqués et entrelacés, du code spaghetti se forme. Lors de l'utilisation du modèle de publication-abonnement, le débogage sera un gâchis.
jQuery est une programmation impérative pour le « processus », et tant de commandes sont finalement utilisées pour mettre à jour les « données » dans l'interface utilisateur, pourquoi ne pas simplement modifier les données directement ?
Pékin → Shanghai, changez simplement city="Beijing" en city="Shanghai". Peu importe que l'avion ou le train tombe en panne à pied, ou que vous rencontriez Wang Baoqiang sur la route, l'importance du cadre frontal moderne réside dans l'innovation d'idées de résolution de problèmes, qui transforme les diverses commandes du « processus » en la description de « état ».
Qu'est-ce que le statut ?L'état correspond à des données dynamiques dans l'interface utilisateur. State in React
À l'ère des composants de classe React, l'état est this.state, qui est mis à jour à l'aide de this.setState.
Pour éviter tout gâchis, React a introduit les concepts de « composants » et de « flux de données unidirectionnel ». Avec les états et les composants, il y a naturellement transfert d'état entre composants, ce que l'on appelle généralement « communication ».
La communication père-enfant est relativement simple, tandis que la communication entre les composants de niveau profond et longue distance repose sur la « promotion du statut » + le transfert d'accessoires couche par couche.
Ainsi, React a introduit Context, une solution officielle pour la communication « entre niveaux » entre les composants.
Mais le contexte équivaut en fait à "l'amélioration du statut", il n'y a pas d'optimisation supplémentaire des performances et son écriture est verbeuse.
Afin d'optimiser les performances, plusieurs contextes sont généralement ajoutés, ce qui rend l'écriture plus verbeuse. Lorsque le projet n’est pas si complexe, ce n’est pas aussi simple que de le faire passer par plusieurs couches.
Qu'est-ce que la « gestion de l'État » ?
Bien sûr, lors de l'utilisation de la bibliothèque de gestion d'état, elle apportera des modèles de pensée dérivés, tels que comment organiser l'état, comment diviser la logique publique, la logique métier, la logique des composants, etc., mais en dernière analyse, ce sont pas les raisons principales.
L'essentiel est de résoudre des problèmes pratiques - pour la communication. Divers autres concepts et philosophies ne sont pas nécessaires.
Context n'est pas si simple à utiliser et il n'existe pas de bonnes pratiques officielles pour React, c'est pourquoi les bibliothèques communautaires sont nées les unes après les autres.
Méthodes de gestion d'état dans React
1. Créer un magasin, un réducteur et un contexte global
src/store/reducer.tsimport 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> ) }
3. Utilisez
src/components/Count/index.tsximport { 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;
1. Créer un état et un réducteur
// 初始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 }
2.写一个发布订阅模式,让组件订阅globalState
src/global-states.ts
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 }
3.组件中使用
src/App.tsx
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> ) }
src/components/Count/index.tsx
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
src/components/Name/index.tsx
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
优缺点分析
由于以上两种都是采用hooks进行状态管理,这里统一进行分析
优点
缺点
使用方法:
1.引入redux
yarn add redux react-redux @types/react-redux redux-thunk
2.新建reducer
在src/store/reducers文件夹下新建addReducer.ts(可建立多个reducer)
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 } }
在src/stores文件夹下新建action.types.ts
主要用于声明action类型
export const ADD = 'ADD' export const DELETE = 'DELETE'
3.合并reducer
在src/store/reducers文件夹下新建index.ts
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
3.创建store
在src/stores文件夹下新建index.ts
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
4.根组件通过 Provider 注入 store
src/index.tsx(用provider将App.tsx包起来)
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') )
5.在组件中使用
src/somponents/Count/index.tsx
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)
src/somponents/Name/index.tsx
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 是一个经过战火洗礼的库,它通过透明的函数响应式编程(transparently applying functional reactive programming - TFRP)使得状态管理变得简单和可扩展。
常规使用(mobx-react)
使用方法
1.引入mobx
yarn add mobx mobx-react -D
2.创建store
在/src/store目录下创建你要用到的store(在这里使用多个store进行演示)
例如:
store1.ts
import { 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
store2.ts
这里使用 makeAutoObservable代替了makeObservable,这样就不用对每个state和action进行修饰了(两个方法都可,自行选择)
import { makeAutoObservable } from 'mobx' class Store2 { constructor() { // mobx6.0之后必须要加上这一句 makeAutoObservable(this) } time = 11111111110 } const store2 = new Store2() export default store2
3.导出store
src/store/index.ts
import store1 from './store1' import store2 from './store2' export const store = { store1, store2 }
4.根组件通过 Provider 注入 store
src/index.tsx(用provider将App.tsx包起来)
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') )
5.在组件中使用
src/somponents/Count/index.tsx
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))
src/components/Name/index.tsx
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))
优缺点分析:
优点:
缺点:
最佳实践(mobx+hooks)
使用方法
1.引入mobx
2.创建store
3.导出store(结合useContext)
src/store/index.ts
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)
4.在组件中使用
无需使用Provider注入根组件
src/somponents/Count/index.tsx
import 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)
src/components/Name/index.tsx
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)
Mobx自动订阅实现原理
基本概念
Observable //被观察者,状态 Observer //观察者,组件 Reaction //响应,是一类的特殊的 Derivation,可以注册响应函数,使之在条件满足时自动执行。
建立依赖
我们给组件包的一层observer实现了这个功能
export default observer(Name)
组件每次mount和update时都会执行一遍useObserver函数,useObserver函数中通过reaction.track进行依赖收集,将该组件加到该Observable变量的依赖中(bindDependencies)。
// 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; }
reaction.track()
_proto.track = function track(fn) { // 开始收集 startBatch(); var result = trackDerivedFunction(this, fn, undefined); // 结束收集 endBatch(); };
reaction.track里面的核心内容是trackDerivedFunction
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 }
触发依赖
Observable(被观察者,状态)修改后,会调用它的set方法,然后再依次执行该Observable之前收集的依赖函数,触发rerender。
组件更新
用组件更新来简单阐述总结一下:mobx的执行原理。
observer这个装饰器(也可以是Hoc),对React组件的render方法进行track。
将render方法,加入到各个observable的依赖中。当observable发生变化,track方法就会执行。
track中,还是先进行依赖收集,调用forceUpdate去更新组件,然后结束依赖收集。
简单总结了一下目前较为常用的状态管理方式,我个人最喜欢的使用方式是Mobx+Hooks,简单轻量易上手。各位可以根据自己的需求选择适合自己项目的管理方式。
【相关推荐:Redis视频教程】
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!