In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces "how to manage the state of react". In the daily operation, I believe that many people have doubts about how to manage the state of react. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful to answer the doubts of "how to manage the state of react". Next, please follow the editor to study!
React state management tools: 1, use hooks for state management; 2, use Redux for state management, this way of supporting tools are relatively complete, you can customize a variety of middleware; 3, use Mobx for state management, which makes state management simple and scalable through transparent function responsive programming.
The operating environment of this tutorial: Windows7 system, react17.0.1 version, Dell G3 computer.
What is "status"?
In the era of jQuery, JS code is mixed with DOM structure, and when each process is complex and intertwined, noodle code is formed, and when using the publish and subscribe model, debugging will be a mess.
JQuery is imperative programming for "procedures", and so many commands are ultimately designed to update the "data" in UI, so why not just change the data?
Beijing → Shanghai, just change city= "Beijing" into city= "Shanghai". No matter whether the plane or train breaks down on foot, whether or not you will meet Wang Baoqiang on the way.
The significance of the modern front-end framework is the innovation of problem-solving ideas, changing the various commands of "process" into the description of "state".
What is status? A state is dynamic data in UI.
Status in React
React was born in May 2013. But until 2015, it will probably be dominated by jQuery. React 0.13.0 was released in March 2015 with the introduction of class component writing.
In the age of React class components, the state is this.state, updated with this.setState.
To avoid a mess, React introduced the concepts of "components" and "one-way data flow". With state and components, there is naturally the transfer of state between components, which is generally called "communication".
Parent-son communication is relatively simple, while the communication of deep-level and long-distance components depends on "state promotion" + props layer-by-layer transmission.
As a result, React introduced Context, an official solution for "cross-level" communication between components.
But Context actually amounts to a "state boost" with no additional performance optimizations and is verbose to write about.
To optimize performance, multiple Context are generally added, which makes it more verbose to write. When the project is not so complex, it is not as simple as layer-by-layer transmission.
What is "state management"?
In terms of pragmatism, "state management" is to solve the "cross-level" communication between components.
Of course, when using the state management library, it will bring some derivative modes of thinking, such as how to organize state, how to split common logic, business logic, component logic, and so on, but in the final analysis, these are not the core reasons.
The core is to solve practical problems-for communication. Other concepts and philosophies are not necessary.
Context doesn't work that well, and React officially doesn't have any best practices, so community libraries are born.
State Management in react
At present, there are three commonly used state management methods: hooks, redux and mobx. Below, I will introduce in detail the use of these three types and analyze their respective advantages and disadvantages for your reference.
Hooks state management
There are two main ways to manage state with hooks:
UseContext+useReducer
UseState+useEffect
UseContext+useReducer
Usage
1. Create store and reducer and global context
Src/store/reducer.ts
Import React from "react"; / / initial state export const state = {count: 0, name: "ry",}; / / reducer is used to modify the state 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)
two。 The root component is injected into context through Provider
Src/App.tsx
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 ()}
3. Use in components
Src/components/Count/index.tsx
Import {GlobalContext} from "@ / store/reducer"; import React, {FC, useContext} from "react"; const Count: FC = () = > {const ctx = useContext (GlobalContext) return (
Count: {ctx.state.count}
Ctx.dispatch ({type: "ModifyCount", payload: ctx.state.count+1}) > + 1);}; export default Count
Src/components/Name/index.tsx
Import {GlobalContext} from "@ / store/reducer"; import React, {FC, useContext} from "react"; const Name: FC = () = > {const ctx = useContext (GlobalContext) console.log ("NameRerendered") return (
Name: {ctx.state.name}
);}; export default Name
UseState+useEffect
Usage
1. Create state and reducer
Src/global-states.ts
/ / initial statelet globalState: GlobalStates = {count: 0, name: 'ry'} / / reducerexport 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 ()}
Src/global-states.type.ts
Export interface GlobalStates {count: number; name: string;} export enum GlobalStatesModificationType {MODIFY_COUNT, MODIFY_NAME}
two。 Write a publish-subscribe model that allows components to subscribe to 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'} / / publish, all subscribers receive messages Perform setState re-rendering 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} / / status change means publishing broadcast ()} / / useEffect + useState to implement publish and subscribe export const useGlobalStates = () = > {const [value NewListener] = useState (globalState) useEffect (() = > {/ / newListener is the new subscriber listeners.push (newListener) / / component uninstall and unsubscribe return () = > {listeners = listeners.filter ((listener) = > listener! = = newListener)}) return value}
3. Used in the component
Src/App.tsx
Import React from 'react'import'. / index.less'import Count from'. / components/Count'import Name from'. / components/Name'export default function () {return ()}
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 = () = > {/ / call useGlobalStates () to subscribe to globalStates () const {count} = useGlobalStates () return (
Count: {count}
ModifyGlobalStates (GlobalStatesModificationType.MODIFY_COUNT, count + 1)} > + 1)} 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 (
Name: {name}
)} export default Count
Analysis of advantages and disadvantages
Since both of the above are managed by hooks, the analysis is unified here.
Advantages
The code is relatively simple, if your project is relatively simple, only a small number of states need to be promoted to the global level, and most components are still managed through local states. At this point, it's good to use hookst for state management. You can't kill a chicken with a knife.
Shortcoming
Both hooks management methods have an obvious drawback, resulting in a large number of invalid rerender, such as the Count and Name components in the example above. When the state.count changes, the Name component also rerender, although it does not use state.count. This is undoubtedly inefficient in large-scale projects.
Redux state management
How to use it:
1. Introduction of redux
Yarn add redux react-redux @ types/react-redux redux-thunk
two。 Create a new reducer
Create a new addReducer.ts under the src/store/reducers folder (multiple reducer can be created)
Import * as types from'.. / action.types'import {AnyAction} from 'redux'// defines parameter API export interface AddState {count: number name: string} / / initializes statelet initialState: AddState = {count: 0, name:' ry'} / / returns a reducerexport default (state: AddState = initialState, action: AnyAction): AddState = > {switch (action.type) {case types.ADD: return {... state Count: state.count + action.payload} default: return state}}
Create a new action.types.ts under the src/stores folder
Mainly used to declare action types
Export const ADD = 'ADD'export const DELETE =' DELETE'
3. Merge reducer
Create a new index.ts under the src/store/reducers folder
Import {combineReducers, ReducersMapObject, AnyAction, Reducer} from 'redux'import addReducer, {AddState} from'. / addReducer'// merge reducers if there are multiple reducer, modular export interface CombinedState {addReducer: AddState} const reducers: ReducersMapObject = {addReducer} const reducer: Reducer = combineReducers (reducers) export default reducer
3. Create store
Create a new index.ts under the src/stores folder
Import {createStore, applyMiddleware, StoreEnhancer, StoreEnhancerStoreCreator, Store} from 'redux'import thunk from' redux-thunk'import reducer from'. / reducers'// generates store intensifier const storeEnhancer: StoreEnhancer = applyMiddleware (thunk) const storeEnhancerStoreCreator: StoreEnhancerStoreCreator = storeEnhancer (createStore) const store: Store = storeEnhancerStoreCreator (reducer) export default store
4. The root component is injected into store through Provider
Src/index.tsx (wrap App.tsx in provider)
Import React from 'react'import ReactDOM from' react-dom'import App from'. / App'import {Provider} from 'react-redux'import store from'. / store'ReactDOM.render (, document.getElementById ('root'))
5. Use in components
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'// declare parameter interface interface Props {count: number add: (num: number) = > void} / / ReturnType gets the function return value type & Cross type (for multi-type merging) / / type Props = ReturnType & ReturnTypeconst Count: FC = (props) = > {const {count, add} = props return (
Count: {count}
Add (5)} > addCount)} / / this is equivalent to a manual mapping, only the attribute changes that are mapped to here Component rerenderconst mapStateToProps = (state: CombinedState) = > ({count: state.addReducer.count}) const mapDispatchToProps = (dispatch: Dispatch) = > {return {add (num: number = 1) {/ / payload is the parameter 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'// declaration parameter interface interface Props {name: string} const Name: FC = (props) = > {const {name} = props console.log ('NameRerendered') return (
Name: {name}
)} / / name change component rerenderconst mapStateToProps = (state: CombinedState) = > ({name: state.addReducer.name}) / / any attribute change component in addReducer will rerender// const mapStateToProps = (state: CombinedState) = > state.addReducerexport default connect (mapStateToProps) (Name)
Analysis of advantages and disadvantages
Advantages
The component subscribes to a specific attribute in store [done manually by mapStateToProps]. Only when the attribute changes, the component will rerender, resulting in high rendering efficiency.
Process norms, in accordance with the officially recommended norms and combined with the team style to create a set of their own process.
Supporting tools are relatively complete. Redux-thunk supports asynchronism, and redux-devtools supports debugging.
You can customize all kinds of middleware
Shortcoming
The way of state+action+reducer is not easy to understand and intuitive.
Very verbose, for a function to write both reducer and action, but also to write a file to define actionType, it is very troublesome
The use of body feeling is very poor, each component that uses the global state has to write a mapStateToProps and mapDispatchToProps, and then wrap a layer with connect, I just use a simple state, why is it so complicated?
Of course, there are a lot of import files, 100 lines of code using redux can be changed into 120 lines, but from another point of view, this is also an increase in the amount of code.
It seems that there are no shortcomings except complexity.
Mobx state management
MobX is a war-baptized library that makes state management simple and extensible through transparent functional responsive programming (transparently applying functional reactive programming-TFRP).
General use (mobx-react)
Usage
1. Introduction of mobx
Yarn add mobx mobx-react-D
two。 Create store
Create the store you want to use in the / src/store directory (use multiple store for demonstration here)
For example:
Store1.ts
Import {observable, action, makeObservable} from 'mobx'class Store1 {constructor () {makeObservable (this) / / mobx6.0 must add this sentence @ observable count = 0 @ observable name =' ry' @ action addCount = () = > {this.count + = 1}} const store1 = new Store1 () export default store1
Store2.ts
Here makeAutoObservable is used instead of makeObservable, so there is no need to modify each state and action (either method is available, choose by yourself)
Import {makeAutoObservable} from 'mobx'class Store2 {constructor () {/ / mobx6.0 must add this sentence makeAutoObservable (this)} time = 11111111110} const store2 = new Store2 () export default store2
3. Export store
Src/store/index.ts
Import store1 from'. / store1'import store2 from'. / store2'export const store = {store1, store2}
4. The root component is injected into store through Provider
Src/index.tsx (wrap App.tsx in provider)
Import React from 'react'import ReactDOM from' react-dom'import App from'. / App'import store from'. / store'import {Provider} from 'mobx-react'ReactDOM.render (, document.getElementById (' root'))
5. Use in components
Src/somponents/Count/index.tsx
Import React, {FC} from 'react'import {observer, inject} from' mobx-react'// class components are injected with a decorator as follows: / / @ inject ('store1') / / @ observerinterface Props {store1?: any} const Count: FC = (props) = > {const {count, addCount} = props.store1 return (
Count: {count}
AddCount)} / / function components use Hoc as follows (this article uniformly uses function components) 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) = > {const {name} = props.store1 console.log ('NameRerendered') return (
Name: {name}
)} / / Hoc is used for function components as follows (this article uniformly uses function components) export default inject ('store1') (observer (Name))
Analysis of advantages and disadvantages:
Advantages:
The component automatically subscribes to a specific property in store, without the need for manual subscription. [the principle will be briefly described below] only when the properties of the subscription change, the component will rerender, resulting in high rendering efficiency.
A store writes both state and action, which is easy to understand and has less code.
Disadvantages:
When the technology stack we choose is React+Typescript+Mobx, this method has a very obvious disadvantage. The introduced store can only be used after the type or interface of the props is defined (it will add a lot of code), and the store must be specified as optional, otherwise it will report an error (because the parent component does not actually pass this prop to the child component). Doing so may also cause the prompt to be undefined when the value of store is taken. Although you can use "!" Rule out undefined, but this is not elegant.
Best practices (mobx+hooks)
Usage
1. Introduction of mobx
Same as above
two。 Create store
Same as above
3. Export store (combined with useContext)
Src/store/index.ts
Import React from 'react'import store1 from'. / store1'import store2 from'. / store2'// Export store1export const storeContext1 = React.createContext (store1) export const useStore1 = () = > React.useContext (storeContext1) / / Export store2export const storeContext2 = React.createContext (store2) export const useStore2 = () = > React.useContext (storeContext2)
4. Use in components
No need to use Provider to inject the root component
Src/somponents/Count/index.tsx
Import React, {FC} from 'react'import {observer} from' mobx-react'import {useStore1} from'@ / store/'// class components are available decorators as follows: / / @ observerconst Count: FC = () = > {const {count, addCount} = useStore1 () return (
Count: {count}
AddCount)} / / Hoc is used for function components, and the method is as follows (this article uniformly uses function components) 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 (
Name: {name}
)} export default observer (Name) advantages and disadvantages analysis: advantages:
The cost of learning is low, the basic knowledge is very simple, and the same core principle as Vue, responsive programming.
A store writes both state and action, which is easy to understand.
The component automatically subscribes to a specific attribute in store, and only when the attribute changes, the component will rerender, resulting in high rendering efficiency.
Successfully avoided the shortcomings of the previous way of use, without the need for an interface or type declaration of the store used!
Built-in asynchronous action operation mode
The amount of code is really very small, the use is very simple, there is no, highly recommended!
Disadvantages:
Too free: Mobx provides very little convention and template code, which leads to free development code. If you don't make some conventions, it's easier to lead to inconsistent team code style. The team recommends strict mode!
The way to use it is too simple
Implementation principle of Mobx automatic subscription
Basic concept
Observable / / observed, state Observer / / observer, component Reaction / / response, is a special class of Derivation that registers the response function to execute automatically when the condition is met.
Establish dependence
We implemented this function for the observer layer of the component package.
Export default observer (Name)
The component executes the useObserver function every time it mount and update, and the useObserver function collects dependencies through reaction.track, adding the component to the bindDependencies of the Observable variable.
/ / 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) {/ / start collecting startBatch (); var result = trackDerivedFunction (this, fn, undefined); / / end collecting endBatch ();}
The core content of reaction.track is trackDerivedFunction.
Function trackDerivedFunction (derivation: IDerivation, f: () = > T, context: any) {. Let result / / executes callback f, which triggers the get of the variable (that is, the parameter of the component) Thus get dep [Collection dependency] if (globalState.disableErrorBoundaries = true) {result = f.call (context)} else {try {result = f.call (context)} catch (e) {result = new CaughtException (e)} globalState.trackingDerivation = prevTracking / / bind derivation bindDependencies (derivation) to observable. Return result}
Trigger dependency
When the Observable (observer, state) is modified, its set method is called, and then the previously collected dependency functions of the Observable are executed in turn, triggering rerender.
Component update
Use component updates to briefly describe and summarize: how mobx works.
Observer, the decorator (or Hoc), track the render method of the React component.
Add the render method to the dependencies of each observable. When the observable changes, the track method executes.
In track, do dependency collection first, call forceUpdate to update the component, and then end dependency collection.
The reason for dependency collection every time is that dependencies may change each time they are executed.
At this point, the study on "how to manage the state of react" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.