React項目狀態(tài)管理選型
- 全面擁抱typescript,需要對typescript友好慕爬。
- 使用簡單谢鹊,不要太復雜,輕量。
- 支持模塊化,支持集中管理。
- 使用人數(shù)多刁愿。
常用redux對比
下載量前三的三個狀態(tài)管理庫
庫名 | github start | github used | 周下載量 |
---|---|---|---|
React Redux | 22.2k | 2.1m | 5,718,723 |
redux-toolkit | 8.1k | 274k | 1,664,877 |
mobx | 25.4k | 106k | 903,357 |
去年7月份redux-toolkit與mobx下載量情況如下
庫名 | github start | github used | 周下載量 |
---|---|---|---|
redux-toolkit | 5.9k | 83.2k | 755,564 |
mobx | 23.9k | 83.9k | 671,787 |
分析:react-redux下載量,使用量均排第一到逊,主要原因應該是先發(fā)優(yōu)勢铣口,很多其他狀態(tài)管理器庫依賴react-redux滤钱,tookit是官方狀態(tài)管理庫,使用量目前最大脑题,并且可以通過指令生成帶有tookit狀態(tài)管理器的項目(npx create-react-app --template redux 菩暗,npx create-react-app --template redux-typescript);mobx也是一個非常優(yōu)秀的react狀態(tài)管理庫旭蠕,主要優(yōu)點是使用簡單停团。
常用redux基本使用
一、redux-toolkit的基本使用
1掏熬、安裝依賴包
npm install @reduxjs/toolkit react-redux
2佑稠、配置store
在store目錄下新建index.ts文件,代碼如下:
import { configureStore } from '@reduxjs/toolkit'
export const store = configureStore({
reducer: {},
})
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
修改主頁index.tsx文件如下:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import { store } from './store/tookit/index';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
// redux-toolkit
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
創(chuàng)建scounterSlice文件旗芬,在store目錄下舌胶,features/counter/counterSlice.ts
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
export interface CounterState {
value: number
}
const initialState: CounterState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload
},
},
})
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
修改store/toolkit目錄下index.ts
import counterReducer from "./features/counter/counterSlice";
export const store = configureStore({
reducer: {
counter: counterReducer
}
});
3、新建Counter組件
目錄:src/conponent/Counter.tsx
import React, { Component } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { decrement, increment, incrementByAmount } from '../store/tookit/features/counter/counterSlice';
import type { RootState } from '../store/tookit/index';
import './counter.css';
type Props = {}
function Counter({ }: Props) {
const count = useSelector((state: RootState) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<div>{count}</div>
<div className='button'>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
</div>
</div>
)
}
export default Counter
4疮丛、在App.tsx引入Counter組件
import React from 'react';
import './App.css';
import Counter from './components/Counter';
function App() {
return (
<div className="App">
<Counter />
</div>
);
}
export default App;
5幔嫂、異步操作
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import type { PayloadAction } from '@reduxjs/toolkit'
export interface CounterState {
value: number
}
const initialState: CounterState = {
value: 0
};
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
}
},
extraReducers(builder) {
builder.addCase(incrementAsync.fulfilled, (state, { payload }) => {
state.value += payload;
})
}
});
export const incrementAsync = createAsyncThunk<number>(
'incrementAsync',
async (): Promise<number> => {
const res: any = await new Promise<number>(r => {
setTimeout(() => {
r(2);
}, 2000);
});
return res;
}
)
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
// import React, { Component } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { decrement, increment, incrementByAmount, incrementAsync } from '../store/tookit/features/counter/counterSlice';
import type { AppDispatch, RootState } from '../store/tookit/index';
import './counter.css';
// type Props = {}
function Counter() {
const count = useSelector((state: RootState) => state.counter.value);
const dispatch: AppDispatch = useDispatch();
return (
<div>
<div>{count}</div>
<div className='button'>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
<button onClick={() => dispatch(incrementAsync())}>+2</button>
</div>
</div>
)
}
export default Counter
二、mobx基本使用
1誊薄、安裝mobx依賴包
- mobx:核心庫
- mobx-react-lite:僅支持函數(shù)組件
- mobx-react:既支持函數(shù)組件履恩,也支持函數(shù)組件
npm install mobx mobx-react-lite
2、新建store目錄呢蔫,再建counter目錄切心,在該目錄下新建counter.store.ts
import { makeAutoObservable } from "mobx";
// 1、定義數(shù)據(jù)
export class CounterStore {
count: number = 0;
constructor() {
makeAutoObservable(this);
}
increment() {
this.count++;
}
decrement() {
this.count--;
}
}
3片吊、在store根目錄绽昏,在目錄下面新建index.ts統(tǒng)一管理store
import { CounterStore } from './counter/counter.store';
import React from 'react';
class RootStore {
public counterStore: any;
constructor() {
this.counterStore = new CounterStore();
}
}
// 實例化操作
const rootStore = new RootStore();
// 使用react context機制 完成統(tǒng)一方法封裝;
// Provider value ={傳遞的數(shù)據(jù)}
// 查找機制:useContext 優(yōu)先從Provider value找 如果找不到 就會手
// createContet方法傳遞過來的默認參數(shù)
const context = React.createContext(rootStore);
// 這個方法作用:通過useContext拿到rootStorre實例對象 然后返回
// 只要在業(yè)務組件中 調(diào)用useStore() -> rootStore
const useStore = () => React.useContext(context);
export { useStore };
4俏脊、使用store
新建MobxCounter.tsx組件全谤,代碼如下
import { observer } from 'mobx-react-lite';
import React from 'react'
import './counter.css';
import { useStore } from '../store/mobx/index';
type Props = {}
function MobxCounter({ }: Props) {
const { counterStore } = useStore();
return (
<div>
<div>{counterStore.count}</div>
<div className='button'>
<button onClick={() => counterStore.increment()}>+</button>
<button onClick={() => counterStore.decrement()}>-</button>
</div>
</div>
)
}
// 使用observer是關鍵
export default observer(MobxCounter)
三、redux-react基本使用
1爷贫、安裝依賴
npm i redux react-redux @types/react-redux
2认然、新建常量文件store/redux/contant/index.ts,代碼如下:
export const ADD = 'ADD';
export type ADD = typeof ADD;
export const LESSEN = 'LESSEN';
export type LESSEN = typeof LESSEN;
3沸久、新建action文件store/redux/actions/index.ts季眷,代碼:
import { ADD, LESSEN } from "../constants";
export interface ADDAction {
type: ADD
}
export interface LESSENAction {
type: LESSEN
}
export type ModifyAction = ADDAction | LESSENAction;
// 增加state次數(shù)的方法
export const add = ():ADDAction => ({
type: ADD
})
export const lessen = ():LESSENAction => ({
type: LESSEN
})
4、新建counter.reducer.ts文件
import { ModifyAction } from '../actions/index';
import { ADD, LESSEN } from '../constants/index';
export const counterReducer = (state = 0, action: ModifyAction) => {
switch (action.type) {
case ADD:
return state + 1;
case LESSEN:
return state - 1;
default:
return state;
}
}
5卷胯、在store/redux目錄下面新建index.ts文件,用于集中管理reducer
import { createStore } from "redux";
import { rootReducer } from './reducers/index';
const store = createStore(rootReducer);
export default store;
6威酒、新建ReduxCounter.tsx組件
import React from 'react'
import './counter.css';
import { connect } from 'react-redux'
import { Dispatch } from 'redux'
import { add, lessen } from '../store/redux/actions/index';
type Props = {
// value: number,
// onIncrement: () => void,
// onDecrement: () => void
}
function ReduxCounter(props: any) {
const { value, onadd, onlesen } = props;
return (
<div>
<div>{value.counter}</div>
<div className='button'>
<button onClick={() => onadd()}>+</button>
<button onClick={() => onlesen()}>-</button>
</div>
</div>
)
}
// mapStateToprops 方法用于映射狀態(tài)窑睁;
const mapStateToProps = (state: number): { value: number } => ({
value: state
})
// mapDispatchToProps 方法用于映射操作狀態(tài)的方法
const mapDispatchToProps = (dispatch: Dispatch) => ({
onadd: () => dispatch(add()),
onlesen: () => dispatch(lessen())
})
export default connect(mapStateToProps, mapDispatchToProps)(ReduxCounter)
7挺峡、在主入口index.tsx添加Provider
import { Provider } from 'react-redux'
import store from './store/redux/index';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
// redux-toolkit redux
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
常用redux總結
redux/toolkit 優(yōu)點
- 流程規(guī)范,需要按照規(guī)范進行開發(fā)担钮。
- 函數(shù)式編程橱赠,在
reducer
中,接受輸入箫津,然后輸出狭姨,不會有副作用發(fā)生,冪等性苏遥。 - 可追蹤性饼拍,很容易追蹤產(chǎn)生 BUG 的原因。
redux 缺點(tookit已解決)
- 配置 Redux store 過于復雜
- 須添加很多軟件包才能開始使用 Redux 做事情
- Redux 有太多樣板代碼
mobx 優(yōu)點
- 開發(fā)簡單田炭,class 中管理 state师抄、action,基于 Proxy 實現(xiàn)的數(shù)據(jù)響應式教硫。
- 使組件更加顆吝端保化拆分,并且業(yè)務更加容易抽象瞬矩。
mobx 缺點
- 過于自由茶鉴,mobx 提供的約定及模版代碼很少,如果團隊不做一些約定景用,容易導致團隊代碼風格不統(tǒng)一蛤铜。