前言
在前端项目中,状态管理是一个非常重要的概念和技术。它用于管理应用程序的数据和状态,并确保不同组件之间的数据同步和一致性。趁抚摸fish的时间整理一下使用过的方案。。
Context API
使用 React 构建应用程序时,Context API 是一种用于在组件之间共享状态的机制。它允许你在组件树中传递数据,而不需要手动通过 props 一层层传递。Context API 在某些场景下可以作为全局状态管理的替代方案,尤其适用于较小规模的应用程序或对状态共享需求较简单的情况。
Context API 讲解
- 创建一个 Context组件(MyContext.js): 通过
createContext
函数来实现
js
import React from 'react';
const MyContext = React.createContext();
export default MyContext;
- 提供数据: 接下来,你需要在组件树的某个位置提供数据,使得其他组件可以访问到这个数据。这通常在应用程序的根组件(App.js)中完成。你可以使用
Context.Provider
组件来提供数据,并将数据作为value
属性传递给它。
js
import React from 'react';
import MyContext from './MyContext';
import ChildComponent from './ChildComponent';
const data = { name: 'John', age: 25 };
function App() {
return (
<MyContext.Provider value={data}>
<ChildComponent />
</MyContext.Provider>
);
}
export default App;
- 使用数据: 一旦您在组件树中提供了数据,其他组件就可以通过用
Context.Consumer
或useContext
钩子来访问该数据
-
使用
Context.Consumer
: 在类组件中,你可以使用Context.Consumer
组件来访问提供的数据。它接受一个函数作为子元素,并将提供的数据作为该函数的参数。例如:jsclass ChildComponent extends React.Component { render() { return ( <MyContext.Consumer> {data => <div>{data.name}</div>} </MyContext.Consumer> ); } }
-
使用
useContext
钩子: 在函数式组件中,你可以使用useContext
钩子来访问提供的数据。它接受一个 Context 对象作为参数,并返回当前提供的值。例如:jsfunction ChildComponent() { const data = React.useContext(MyContext); return <div>{data.name}</div>; }
在上面的示例中,ChildComponent
组件可以访问到通过 MyContext.Provider
提供的数据,并在组件中使用它。
Redux
Redux 是一个独立的状态管理库。Redux 的核心概念是单一的全局状态树,通过定义 action、reducer 和 store,可以实现可预测的应用程序状态,通过使用纯函数来管理状态的变化。
Redux讲解
三大核心原则
Redux 遵循以下三个核心原则:
- 单一数据源:整个应用的状态被存储在一个单一的 JavaScript 对象中,称为"state"。
- 状态是只读的:唯一改变状态的方式是通过触发一个"action",一个描述发生什么事件的普通 JavaScript 对象。
- 使用纯函数来执行状态修改:使用"reducers",纯函数,根据当前状态和一个 action 来计算新的状态。
核心概念
- Store:Redux 的状态存储库,用于存储应用的整个状态树。
- State:应用的状态,存储在 Store 中。
- Action :描述发生事件的普通 JavaScript 对象,包含一个
type
字段和其他自定义字段。 - Reducer:纯函数,接收当前状态和一个 action,返回一个新的状态。Reducers 用于根据 action 更新状态。
- Dispatch :通过调用
store.dispatch(action)
来触发一个 action,并将其传递给 reducer。 - Subscribe :通过调用
store.subscribe(listener)
来注册一个监听器,当状态改变时会被调用。
数据流
Redux 的数据流是单向的,遵循以下顺序:
- 调用
store.dispatch(action)
来触发一个 action。 - Redux 调用注册的 reducers,并将当前状态和 action 传递给它们。
- Reducers 根据 action 的类型来更新状态,并返回一个新的状态。
- Redux 更新存储在 Store 中的状态。
- 如果有注册监听器,Redux 调用它们,以便通知状态的改变。
- 用户界面根据新的状态进行更新。
代码示例( Redux Toolkit)
Redux Toolkit 是为了简化和提高 Redux 开发体验而设计的,并且尽可能减少样板代码的编写。它提供了一组工具和约定,使得编写和组织 Redux 代码更加简单和高效。
依赖
首先,确保已经安装了Redux和Redux Toolkit。可以使用以下命令进行安装:
sql
npm install redux @reduxjs/toolkit --save
目录结构
markdown
- src
- counterSlice.js
- index.js
- App.js
- components
- Counter.js
counterSlice.js
使用createSlice
函数创建了一个名为counter
的Slice(Slice 是一个包含 reducer、action 和 action creator 的单个模块化单位)。name
属性指定了Slice的名称,initialState
属性设置了计数器的初始值为0。reducers
对象定义了三个reducer函数:increment
、decrement
和incrementByAmount
,用于增加、减少和按指定值增加计数器的值。
然后,导出了这三个action creator函数:increment
、decrement
和incrementByAmount
,以及reducer函数
js
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment(state) {
return state + 1;
},
decrement(state) {
return state - 1;
},
incrementByAmount(state, action) {
return state + action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
index.js
应用程序的主文件中, 导入了必要的依赖项,包括configureStore
函数和Provider
组件。然后,导入了之前创建的counterReducer
,并将其作为counter
键的值传递给configureStore
函数,以创建Redux store。最后,使用ReactDOM.render
函数将App
组件包装在Provider
组件中,并将Redux store传递给Provider
组件。
js
import React from 'react';
import ReactDOM from 'react-dom';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import counterReducer from './counterSlice';
import App from './App';
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
App.js
创建一个React组件文件,命名App.js
,使用useSelector
钩子从Redux store中选择计数器的值。然后,使用useDispatch
钩子获取一个dispatch函数,以便可以触发Redux action。
接下来,定义了三个事件处理函数:handleIncrement
、handleDecrement
和handleIncrementByAmount
,它们分别通过调度相应的Redux action来增加、减少和按指定值增加计数器的值。
最后,在组件的渲染部分显示了计数器的值,并为每个按钮添加了相应的点击事件。
js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, incrementByAmount } from './counterSlice';
function App() {
const count = useSelector((state) => state.counter);
const dispatch = useDispatch();
const handleIncrement = () => {
dispatch(increment());
};
const handleDecrement = () => {
dispatch(decrement());
};
const handleIncrementByAmount = () => {
dispatch(incrementByAmount(5)); // 可以传递任意增加的值
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>Increment</button>
<button onClick={handleDecrement}>Decrement</button>
<button onClick={handleIncrementByAmount}>Increment by 5</button>
</div>
);
}
export default App;
umi框架实现
(其实Umi 推荐并内置了 Dva 提供了一套状态管理方案,我也试了下,哈哈哈。。)
content 高阶函数可以将 Redux 的状态和操作注入到组件中,使得组件可以方便地访问和操作 Redux 的数据。
通过使用 connect
,你可以在组件中声明所需的状态和操作,而不需要手动编写订阅和分发逻辑。connect
会自动处理与 Redux 的交互,简化了 Redux 的使用过程,减少了代码的编写。
arduino
import { connect } from 'umi';
src目录下的数据文件models文件下新建一个counter.js文件
js
const CounterModel = {
state: {
count: 0,
},
reducers: {
increment(state) {
return { ...state, count: state.count + 1 };
},
decrement(state) {
return { ...state, count: state.count - 1 };
},
incrementByAmount(state, payload) {
return { ...state, count: state.count + payload.num };
},
},
};
export default CounterModel;
在你使用的组件里按如下使用
js
import React from 'react';
import { connect } from 'umi';
function Counter({ count, dispatch } :any) {
const handleIncrement = () => {
dispatch({ type: 'counter/increment' });
};
const handleDecrement = () => {
dispatch({ type: 'counter/decrement' });
};
const handleIncrementByAmount = () => {
dispatch({ type: 'counter/incrementByAmount', num: 5 }); // 可以传递任意增加的值
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>Increment</button>
<button onClick={handleDecrement}>Decrement</button>
<button onClick={handleIncrementByAmount}>Increment by 5</button>
</div>
);
}
export default connect(({ counter }: any) => ({ count: counter.count }))(Counter);
MobX
MobX是一个简单、可扩展和高效的状态管理库,它使用观察者模式来跟踪状态的变化并自动更新相关的组件
Mobx讲解
核心概念
MobX 的核心概念包括以下几个关键点:
- 可观察数据(Observable) :可观察数据是 MobX 的基础概念之一。可观察数据是指被标记为可观察的状态,当这些数据发生变化时,MobX 将自动追踪并通知相关的观察者。在 MobX 中,可以使用
observable
装饰器或observable
函数来定义可观察数据。 - 动作(Actions) :动作是一种更改可观察数据的操作。在 MobX 中,使用
action
装饰器或action
函数来标记方法为动作。当动作被调用时,MobX 将跟踪该动作对可观察数据的修改,并确保触发相应的更新。 - 观察者(Observer) :观察者是指订阅可观察数据变化的组件或函数。在 MobX 中,可以使用
observer
函数或@observer
装饰器将组件转换为观察者组件。观察者会自动订阅可观察数据的变化,并在数据发生变化时自动重新渲染。 - 自动追踪(Auto-tracking) :自动追踪是 MobX 的一个重要特性。当可观察数据被访问时,MobX 将自动追踪数据的依赖关系,并建立观察者与可观察数据之间的连接。这意味着当数据发生变化时,相关的观察者将自动被通知和更新。
- 衍生数据(Computed) :衍生数据是根据可观察数据计算得出的数据。在 MobX 中,可以使用
computed
装饰器或computed
函数来定义衍生数据。衍生数据具有自动缓存和惰性计算的特性,只有在相关的可观察数据发生变化时才会重新计算。
数据流
在 MobX 中,数据流遵循以下基本流程:
- 定义可观察数据(Observable Data) :通过使用
observable
装饰器或observable
函数,我们可以将数据标记为可观察的。这意味着当数据发生变化时,MobX 将自动追踪这些变化。 - 定义动作(Actions) :通过使用
action
装饰器或action
函数,我们可以将方法标记为动作。动作是一种更改状态的操作,它们可以修改可观察数据。 - 创建观察者组件(Observer Components) :通过使用
observer
函数或@observer
装饰器,我们可以将 React 组件转换为观察者组件。观察者组件订阅了可观察数据的变化,并在数据发生变化时自动重新渲染。 - 自动追踪数据变化(Automatic Tracking) :一旦数据发生变化,MobX 将自动追踪和记录数据的变化。这意味着任何观察者组件订阅的数据将自动更新,并进行相应的重新渲染。
- 通知观察者(Observer Notification) :当数据发生变化时,MobX 将自动通知所有相关的观察者组件。这样,观察者组件就能够根据新的数据进行更新和渲染。
代码示例
依赖
首先,确保已经安装了mobx mobx-react。可以使用以下命令进行安装
bash
npm install mobx mobx-react --save
目录结构
在这个目录结构中,src
是源代码根目录,components
目录用于存放应用程序的组件,stores
目录用于存放 MobX 的状态管理类
css
src/
|- components/
| |- Counter.js
|
|- stores/
|- CounterStore.js
CounterStore.js
这是一个 MobX 的状态管理类文件,负责定义和管理计数器的状态和行为
js
import { observable, action } from 'mobx';
class CounterStore {
@observable count = 0;
@action increment() {
this.count++;
}
@action decrement() {
this.count--;
}
}
const counterStore = new CounterStore();
export default counterStore;
Counter.js
这是一个 React 组件文件,负责渲染计数器界面和处理用户操作。它使用 MobX 的状态管理类来获取和更新计数器的值
js
import React from 'react';
import { observer } from 'mobx-react';
import counterStore from '../stores/CounterStore';
const Counter = observer(() => {
return (
<div>
<h2>Count: {counterStore.count}</h2>
<button onClick={counterStore.increment}>Increment</button>
<button onClick={counterStore.decrement}>Decrement</button>
</div>
);
});
export default Counter;