React의 상태 관리 도구: 1. 상태 관리를 위해 후크를 사용합니다. 2. 상태 관리를 위해 Redux를 사용합니다. 이 방법은 비교적 완전한 지원 도구 세트를 갖추고 있으며 다양한 미들웨어를 사용자 정의할 수 있습니다. 3. 상태 관리를 위해 Mobx를 사용합니다. 투명한 기능을 통해 상태 관리를 간단하고 확장 가능하게 만듭니다.
이 튜토리얼의 운영 환경: Windows 7 시스템, 반응 버전 17.0.1, Dell G3 컴퓨터.
JQuery 시대에는 JS 코드에 DOM 구조가 섞여 있고, 여러 프로세스가 복잡하고 얽혀 있으면 게시-구독 모델을 사용하면 디버깅이 엉망이 됩니다.
jQuery는 "프로세스"를 위한 명령형 프로그래밍이므로 궁극적으로 UI의 "데이터"를 업데이트하는 데 사용되는 명령이 너무 많습니다. 데이터를 직접 변경하는 것은 어떨까요?
베이징 → 상하이, city="Beijing"을 city="Shanghai"로 변경하면 됩니다. 비행기나 기차가 걸어서 고장이 나든, 길에서 Wang Baoqiang을 만나든 상관없이 현대 프론트엔드 프레임워크의 의의는 "프로세스"의 다양한 명령을 "상태"에 대한 설명입니다.
상태란 무엇인가요?상태는 UI의 동적 데이터입니다. React의 상태
React 클래스 컴포넌트 시대에는 상태가 this.state이며, this.setState를 사용하여 업데이트됩니다.
혼란을 피하기 위해 React는 "컴포넌트"와 "단방향 데이터 흐름" 개념을 도입했습니다. 상태와 구성요소의 경우 일반적으로 "통신"이라고 불리는 구성요소 간의 상태 전송이 자연스럽게 이루어집니다.
아버지-자식 통신은 비교적 간단한 반면, 심층 및 장거리 구성 요소 간의 통신은 "상태 승격" + 소품의 레이어별 전송에 의존합니다.
그래서 React는 구성 요소 간의 "교차 수준" 통신을 위한 공식 솔루션인 Context를 도입했습니다.
하지만 Context는 실제로 "상태 개선"과 동일하며 추가적인 성능 최적화가 없으며 장황하게 작성됩니다.
성능을 최적화하기 위해 일반적으로 여러 컨텍스트를 추가하므로 작성이 더 장황해집니다. 프로젝트가 그다지 복잡하지 않을 때는 레이어를 통과하는 것만큼 간단하지 않습니다.
'상태 관리'란 무엇인가요?
물론 상태 관리 라이브러리를 사용하면 상태 구성 방법, 공용 논리 분할 방법, 비즈니스 논리, 구성 요소 논리 등과 같은 몇 가지 파생적 사고 패턴을 가져올 수 있지만 최종 분석에서는 다음과 같습니다. 핵심 이유는 아닙니다.
핵심은 의사소통을 위한 실질적인 문제 해결입니다. 다른 다양한 개념과 철학은 필요하지 않습니다.
Context는 사용하기가 쉽지 않고, React에 대한 공식적인 모범 사례가 없어 커뮤니티 라이브러리가 속속 탄생했습니다.
React의 상태 관리 방법
후크 상태 관리
useContext+useReducer
사용 방법
src/store/reducer.ts
import 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);
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 ( <div> <GlobalContext.Provider value={{state, dispatch}}> <Count /> <Name /> </GlobalContext.Provider> </div> ) }
src/comComponents/Count/index.tsx
import { 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;
src/comComponents/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 ( <div> <p>name:{ctx.state.name}</p> </div> ); }; export default Name;
useState+useEffect를 사용하세요.
사용방법src/global-states.ts
// 初始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() }
src/global-states.type.ts
export interface GlobalStates { count: number; name: string; } export enum GlobalStatesModificationType { MODIFY_COUNT, MODIFY_NAME }
2.写一个发布订阅模式,让组件订阅globalState src/global-states.ts 3.组件中使用 src/App.tsx src/components/Count/index.tsx src/components/Name/index.tsx 优缺点分析 由于以上两种都是采用hooks进行状态管理,这里统一进行分析 优点 缺点 使用方法: 1.引入redux 2.新建reducer 在src/store/reducers文件夹下新建addReducer.ts(可建立多个reducer) 在src/stores文件夹下新建action.types.ts 3.合并reducer 在src/store/reducers文件夹下新建index.ts 3.创建store 在src/stores文件夹下新建index.ts 4.根组件通过 Provider 注入 store src/index.tsx(用provider将App.tsx包起来) 5.在组件中使用 src/somponents/Count/index.tsx src/somponents/Name/index.tsx 优缺点分析 优点 缺点 MobX 是一个经过战火洗礼的库,它通过透明的函数响应式编程(transparently applying functional reactive programming - TFRP)使得状态管理变得简单和可扩展。 常规使用(mobx-react) 使用方法 1.引入mobx 2.创建store 在/src/store目录下创建你要用到的store(在这里使用多个store进行演示) store2.ts 3.导出store src/store/index.ts 4.根组件通过 Provider 注入 store src/index.tsx(用provider将App.tsx包起来) 5.在组件中使用 src/somponents/Count/index.tsx src/components/Name/index.tsx 优缺点分析: 优点: 缺点: 最佳实践(mobx+hooks) 使用方法 1.引入mobx 2.创建store 3.导出store(结合useContext) src/store/index.ts 4.在组件中使用 无需使用Provider注入根组件 src/components/Name/index.tsx Mobx自动订阅实现原理 基本概念 建立依赖 我们给组件包的一层observer实现了这个功能 组件每次mount和update时都会执行一遍useObserver函数,useObserver函数中通过reaction.track进行依赖收集,将该组件加到该Observable变量的依赖中(bindDependencies)。 reaction.track() reaction.track里面的核心内容是trackDerivedFunction 触发依赖 Observable(被观察者,状态)修改后,会调用它的set方法,然后再依次执行该Observable之前收集的依赖函数,触发rerender。 组件更新 用组件更新来简单阐述总结一下:mobx的执行原理。 observer这个装饰器(也可以是Hoc),对React组件的render方法进行track。 将render方法,加入到各个observable的依赖中。当observable发生变化,track方法就会执行。 track中,还是先进行依赖收集,调用forceUpdate去更新组件,然后结束依赖收集。 简单总结了一下目前较为常用的状态管理方式,我个人最喜欢的使用方式是Mobx+Hooks,简单轻量易上手。各位可以根据自己的需求选择适合自己项目的管理方式。 【相关推荐:Redis视频教程】 위 내용은 상태를 관리하기 위해 반응은 무엇을 사용합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!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
}
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>
)
}
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
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
Redux状态管理
yarn add redux react-redux @types/react-redux redux-thunk
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
}
}
主要用于声明action类型export const ADD = 'ADD'
export const DELETE = 'DELETE'
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
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
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')
)
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)
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状态管理
yarn add mobx mobx-react -D
例如:
store1.tsimport { 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
这里使用 makeAutoObservable代替了makeObservable,这样就不用对每个state和action进行修饰了(两个方法都可,自行选择)import { makeAutoObservable } from 'mobx'
class Store2 {
constructor() {
// mobx6.0之后必须要加上这一句
makeAutoObservable(this)
}
time = 11111111110
}
const store2 = new Store2()
export default store2
import store1 from './store1'
import store2 from './store2'
export const store = { store1, store2 }
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')
)
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))
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))
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)
src/somponents/Count/index.tsximport 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)
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)
优缺点分析:
优点:
缺点:
Observable //被观察者,状态
Observer //观察者,组件
Reaction //响应,是一类的特殊的 Derivation,可以注册响应函数,使之在条件满足时自动执行。
export default observer(Name)
// 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;
}
_proto.track = function track(fn) {
// 开始收集
startBatch();
var result = trackDerivedFunction(this, fn, undefined);
// 结束收集
endBatch();
};
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
}
总结