React中状态管理之redux、mobx简单总结

Redux

一、什么是redux:

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。Redux 除了和 React 一起用外,还支持其它界面库。 它体小精悍(只有2kB,包括依赖)。

二、安装与引入:

npm install --save redux //安装

import { createStore } from 'redux' //引入

三、redux三大原则:

单一数据源、state是只读的,使用纯函数来执行修改。

四、使用redux:

当安装好redux包并引入以后,我们通过creteStore(reducer)创建一个store状态机,用于状态管理。

reducer是一个纯函数【纯函数即返回值只有传入的参数决定】,reducer(state,action)有2个参数,state为当前的状态,action 就是一个描述“发生了什么”的普通对象,reducer中通过switch流程控制语句判断action.type的值,来更改state。

通过使用store.dispatch(action)来触发aciton的改变,然后通过store.subscribe(()=>{}),来手动订阅更新,当store.dispatch(action)后,就会触发store.subscribe。通过使用store.getState()获取当前store中state状态。

1、State:

  • DomainState:服务端返回的State;
  • UI State:关于当前组件的State;
  • App State:全局的State;

2、Action事件:

  • 本质就是一个JS对象;
  • 必须包含type属性;
  • 只是描述了有事情要发生,并没有描述如何去更新State;

3、 Reducer:

  • 本质就是函数;
  • 响应发送过来的Action;
  • 函数接收两个参数,第一个是初始化State,另一个是发送过来的Action;
  • 必须要有return返回值;

4、Store:

Store就是把action与reducer联系到一起的对象。

主要职责:

  • 维持应用的state;
  • 提供getState()方法获取state;
  • 提供dispatch()方法发送action;
  • 通过subscribe()方法注册监听;
  • 通过subscribe()返回值注销监听。
Import {createStore} from ‘redux’

const store = createStore(传递的reducer)

五、使用:

项目结构如下:

1、创建一个Action:

  • 在根目录下创建一个文件夹action,在action下创建一个index.js文件用来构建Action:const sendAction = () => {...} ;
  • 在action创建函数里面利用return返回一个action对象,注意要携带type属性:const sendAction  = () => {return {type: ‘send_action’, value: ‘发送了一个action’}}
  • 把这个action创建函数进行导出:
module.exports = { sendAction }

代码:

const sendAction = () => {
    return {
        type: 'send_type',
        value: '这是一个action'
    }
}

module.exports = {
    sendAction
}

2、创建一个Reducer:

  • 在根目录下创建一个reducer目录,在reducer目录下创建一个index.js文件用来构建reducer,注意reducer要有两个接收函数:
const rootReducer = (state,  action) => {...} 
  • 第一个参数是默认状态,我们可以定义一个初始化的state,然后进行赋值:
const initState = {value: ‘默认值’}
const rootReducer = (state=initState ,  action) => {...} 
  • 在函数里面判断第二个参数action的type值是否是我们需要发送的,如果是的话我们可以通过return返回新的state;
  • 把reducer进行导出;

代码:

const initState = {
    value: "默认值"
}

const reducer = (state = initState, action) => {
    switch(action.type){
        case "send_type":
            return Object.assign({}, state, action) ;
        default: 
            return state;
    }
}

module.exports = {
    reducer
}

3、构建Store:

  • 在根目录下创建store文件夹,在store文件夹下创建index.js文件来构建store,注意createStore函数里面第一个参数是reducer:
import {createStore} from ‘redux’
// 在此导入创建的reducer
const store  =  createStore(reducer)
  • createStore返回值就是我们创建的store;
  • 将创建的store进行导出;

代码:

import {createStore} from 'redux'
import {reducer} from '../reducer'

const store = createStore(reducer);

export default store;

4、在组件中使用:

  • 给组件ButtonCom.js中的button按钮绑定一个点击事件;
  • 在组件一加载完毕的时候我们通过store来进行监听器的注册,返回值可以用来注销监听:
this.unSubscribe = store.subscribe(()=>{...})
  • 在点击事件函数处理中通过store.dispatch来发送一个action:
handle = () => {
store.dispatch(sendAction());
}

代码:

import React from 'react'
import { Button } from 'antd';
import { RedoOutlined } from '@ant-design/icons';
import store from './store'
import {sendAction} from './action'

export default class MenuCom extends React.Component {
    constructor(props) {
        super(props);
        //subscribe当store中数据发生变化就会更新数据
        store.subscribe(function () {
            this.setState({
                storeValue: store.getState()
            })
        })
    }

    state = {
        storeValue: store.getState()
    }

    /*onSubscribe = () => {
        this.setState({
            storeValue: store.getState()
        })
    }*/

    componentWillMount = () => {
        store.subscribe(this.onSubscribe)
    }

    handleClick = ()=>{
        const action = sendAction();
        store.dispatch(action);
    }

    render() {
        return (<>
            <Button type="primary" icon={<RedoOutlined/>} ghost onClick={()=>this.handleClick()}>修改</Button>
            <div>{storeValue.value}</div>
        </>)
    }
}

再看简单的实例:

// npm i redux -S
// npm i react-redux -S

// package.json
//  "dependencies": {
//    "redux": "^5.0.1",
//    "react-redux": "^9.1.2"
//  },

// 1、src/redux/reducer.tsx

const initialState = { value: 0 }

type Action = {
    type: string,
    value?: any
}

function counterReducer(state = initialState, action: Action) {
    if (action.type === 'counter/increment') {
        return {
            ...state,
            value: state.value + 1
        }
    }
    return state
}

export default counterReducer

// 2、src/redux/index.tsx

import { createStore } from "redux"
import reducer from "./reducer"

const store = createStore(reducer)

export default store

// 3、src/index.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import reportWebVitals from "@/reportWebVitals";
import { Provider } from 'react-redux'
import store from '@/redux'
import App from "./App";
import "@/index.css";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);

root.render(
  <BrowserRouter>
    <Provider store={store}>
      <App />
    </Provider>
  </BrowserRouter>

);

reportWebVitals();

// 4、src/pages/home/index.tsx

import React, { useState } from "react";
import { Timeline, Button } from "antd";
import store from "@/redux";

type Obj = {
  value: number
}

const App: React.FC = () => {
  const [obj, setObj] = useState<Obj>({value: 0});

  // 监听Action,数据更新会执行
  store.subscribe(() => {
    setObj(store.getState());
  });


  const increate = () => {
    store.dispatch({ type: "counter/increment" });
    console.log(store.getState());
  };

  return (
    <>
      <div>{obj.value}</div>
      <div>
        <Button type="primary" onClick={increate}>
          Redux实例
        </Button>
      </div>
    </>
  );
};

export default App;

六、总结:

完整示例:https://github.com/samveduan/redux.git

Redux Toolkit

redux-toolkit 是官方推荐的编写redux逻辑的方法,简化了redux的配置过程,无需再创建actions、reducer的,更大程度方便使用redux仓库。

Redux Toolkit 包含了 Redux 核心,以及我们认为对于构建 Redux 应用程序必不可少的其他关键软件包(例如 Redux Thunk 和 Reselect)。

在使用redux-toolkit的时候需要搭配React 绑定 和 开发者工具:

npm install react-redux
npm install --save-dev redux-devtools

redux-toolkit是Redux 5.x版本新增的,我们对比下Redux和Redux Toolkit写法:

Redux:

import { createStore } from 'redux'

/**
 * 这是一个 reducer 函数:接受当前 state 值和描述“发生了什么”的 action 对象,它返回一个新的 state 值。
 * reducer 函数签名是 : (state, action) => newState
 *
 * Redux state 应该只包含普通的 JS 对象、数组和原语。
 * 根状态值通常是一个对象。 重要的是,不应该改变 state 对象,而是在 state 发生变化时返回一个新对象。
 *
 * 你可以在 reducer 中使用任何条件逻辑。 在这个例子中,我们使用了 switch 语句,但这不是必需的。
 * 
 */
function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case 'counter/incremented':
      return { value: state.value + 1 }
    case 'counter/decremented':
      return { value: state.value - 1 }
    default:
      return state
  }
}

// 创建一个包含应用程序 state 的 Redux store。
// 它的 API 有 { subscribe, dispatch, getState }.
let store = createStore(counterReducer)

// 你可以使用 subscribe() 来更新 UI 以响应 state 的更改。
// 通常你会使用视图绑定库(例如 React Redux)而不是直接使用 subscribe()。
// 可能还有其他用例对 subscribe 也有帮助。

store.subscribe(() => console.log(store.getState()))

// 改变内部状态的唯一方法是 dispatch 一个 action。
// 这些 action 可以被序列化、记录或存储,然后再重放。
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}

Redux Toolkit:

Redux Toolkit 简化了编写 Redux 逻辑和设置 store 的过程。 使用 Redux Toolkit,相同的逻辑如下所示:

import { createSlice, configureStore } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    incremented: state => {
      // Redux Toolkit 允许在 reducers 中编写 "mutating" 逻辑。
      // 它实际上并没有改变 state,因为使用的是 Immer 库,检测到“草稿 state”的变化并产生一个全新的
      // 基于这些更改的不可变的 state。
      state.value += 1
    },
    decremented: state => {
      state.value -= 1
    }
  }
})

export const { incremented, decremented } = counterSlice.actions

const store = configureStore({
  reducer: counterSlice.reducer
})

// 可以订阅 store
store.subscribe(() => console.log(store.getState()))

// 将我们所创建的 action 对象传递给 `dispatch`
store.dispatch(incremented())
// {value: 1}
store.dispatch(incremented())
// {value: 2}
store.dispatch(decremented())
// {value: 1}

一个小实例:

1、安装:

npm i @reduxjs/toolkit react-redux

2、src/redux/reducer.tsx

同步配置

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:CounterState) => {
      state.value += 1
    },
    decrement: (state:CounterState) => {
      state.value -= 1
    },
  },
})

// 这里的actions对应的是上面的reducers function
export const { increment, decrement } = counterSlice.actions

export default counterSlice.reducer

 异步配置

import { createSlice,createAsyncThunk } 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: {
   	......
  },
  // 异步操作
  extraReducers(builder){
    // 这里的payload就是incrementAsync的返回值
    builder.addCase(incrementAsync.fulfilled,(state,{payload})=>{
      state.value += payload
    })
  }
})
// 定义异步函数
export const incrementAsync = createAsyncThunk(
  "incrementAsync",
  async(p:number)=>{
  const res = await new Promise<number>(r=>{
    setTimeout(() => {
      r(p)
    }, 1000);
  })
  return res
})

export const { incrementAsync } = counterSlice.actions

export default counterSlice.reducer

3、src/redux/index.tsx中注册

import { configureStore } from "@reduxjs/toolkit";
// 引入counterSlice.ts
import counterStore  from "@/redux/reducer";

const store = configureStore({
  reducer:{
  	// 注册,注意名字要与counterSlice.ts中的name一致
    counter:counterStore
  }
})

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

export default store

 4、src/index.tsx中注册store

import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import reportWebVitals from "@/reportWebVitals";
import { Provider } from 'react-redux'
import store from '@/redux'
import App from "./App";
import "@/index.css";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);

root.render(
  <BrowserRouter>
    <Provider store={store}>
      <App />
    </Provider>
  </BrowserRouter>

);

reportWebVitals();

5、使用:/src/pages/app.tsx

import React from "react";
// useSelect用于选择操作哪个store
// useDispatch用于生成实例,该实例可以调用reducers的function
import { useSelector, useDispatch } from "react-redux"
// 引入index中的RootState类型  js项目不需要
import { AppDispatch, RootState } from "@/redux/"
// 引入functions
import { decrement, increment } from "@/redux/reducer"

const App: React.FC = () => {
  const count = useSelector((state: RootState) => state.counter.value)
  const dispatch: AppDispatch = useDispatch() // 异步函数必须加AppDispatch类型,否则报错,同步可以不添加

  return (
    <>
      <div>{count}</div>
      <button onClick={() => dispatch(increment())}>+1</button>
      <button onClick={() => dispatch(decrement())}>-1</button>
    </>
  )
};
export default App;

简化写法:

1、src/redux/index.tsx

import { createSlice, configureStore } from "@reduxjs/toolkit";

export interface counterState {
  value: number;
}

const counterSlice = createSlice({
  name: "counter",
  initialState: {
    value: 0
  },
  reducers: {
    incremented: (state: counterState) => {
      state.value += 1;
    },
    decremented: (state: counterState) => {
      state.value -= 1;
    }
  }
});

export const { incremented, decremented } = counterSlice.actions;

const store = configureStore({
  reducer: counterSlice.reducer
});

export default store;

2、src/index.tsx中注册store

import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import reportWebVitals from "@/reportWebVitals";
import { Provider } from 'react-redux'
import store from '@/redux'
import App from "./App";
import "@/index.css";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);

root.render(
  <BrowserRouter>
    <Provider store={store}>
      <App />
    </Provider>
  </BrowserRouter>

);

reportWebVitals();

3、使用:/src/pages/app.tsx

import React, {useState} from "react";
import store, { incremented, decremented } from "@/redux/"

const App: React.FC = () => {
  const [value, setValue] = useState(0)
  store.subscribe(() => setValue(store.getState().value))

  return (
    <>
      <div>{value}</div>
      <button onClick={() => store.dispatch(incremented())}>+1</button>
      <button onClick={() => store.dispatch(decremented())}>-1</button>
    </>
  )
};

export default App;

Mobx:

Mobx的使用方法:Mobx的使用方法_mobx使用步骤-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值