0
点赞
收藏
分享

微信扫一扫

react redux中间件使用及原理

使用create-react-app创建项目,最终的项目结构如下:

react redux中间件使用及原理_自定义中间价

下面介绍各个文件

package.json

{
  "name": "redux-toolkit-demo",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@reduxjs/toolkit": "^2.1.0",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.6.7",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-redux": "^9.1.0",
    "react-scripts": "5.0.1",
    "redux": "^5.0.1",
    "redux-logger": "^3.0.6",
    "redux-thunk": "^3.1.0",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

使用如下命令安装项目所需的依赖包

npm install  

./src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
//配合自定义的connect函数
import { StoreContext } from './hoc';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
//配合使用react-redux提供的connect函数
  <Provider store={store}>
  //配合使用自定义的高阶函数connect
    <StoreContext.Provider value={store}>
      <App />
    </StoreContext.Provider>
  </Provider>
);

./src/app.js

import Profile from "./views/Profile";
import Home from "./views/Home";
import About from "./views/About";
import './style.css'
import { useSelector } from "react-redux";
function App(props) {
  const {number}= useSelector(state =>{
     
    return state.counter
  })
  
  return (
    <div className="App">
        <h2>
        App count:{number}
        </h2>
        <div className="c1">
          <Home/>
          <Profile/>
          <About />

        </div>
    </div>
  );
}

export default App;

./src/style.css

.c1 {
    display: flex;
}
.c1  >div {
    flex: 1;
    border: 1px dashed #F00;
    padding: 10px;
}

./src/hoc

该文件夹实现自定义的高阶函数connect

 StoreContext.js

import { createContext } from "react"
export const StoreContext= createContext()

 customizeConnect.js

import React, { PureComponent } from 'react'
//import store from '../store'  store与项目解耦,只需要传入StoreContext即可,和react-redux的Provider一致
import { StoreContext } from './StoreContext'
/* 
connect 的参数
参数1:函数
参数2:函数
返回值:函数=》高阶组件
*/
export  function connect(mapStateToProps,mapDispatchToProps){
   return function(WrappedComponent){
    class NewComponent extends PureComponent{
        constructor(props,context){
            super(props)
            this.state=mapStateToProps(context.getState())
       
        }
        componentDidMount(){
            this.unsubscribe =  this.context.subscribe(()=>{
                this.setState(mapStateToProps( this.context.getState()))
            })
        }
        componentWillUnmount(){
            this.unsubscribe()
        }

        render(){
            const stateObj = mapStateToProps( this.context.getState())
            const dispatchObj = mapDispatchToProps( this.context.dispatch)
            return <WrappedComponent {...this.props}{...stateObj}{...dispatchObj} />
        }
    }
    NewComponent.contextType= StoreContext
   // NewComponent.contextType= {store:StoreContext}
   
 return NewComponent
   }
}
 

index.js

export {StoreContext} from './StoreContext'
export {connect} from './customizeConnect'

src\store\

该文件夹与store相关

index.js

创建store可以使用redux提供的createStore,也可以使用reduxjs/toolkit的configureStore

import { configureStore } from "@reduxjs/toolkit";
import counterReducer from './features/counter'
import { createStore, applyMiddleware, compose, combineReducers } from "redux";
import { thunk } from 'redux-thunk'
import peomReducer from './features/poem'
import {reduxlogger,customizethunk,applyMiddlewareCustomize} from './middleware'
// 调用configureStore默认使用了
// 1. redux-thunk中间件来支持异步action,
// 2. redux-devtools-extension来支持ReduxDevTools浏览器扩展,
// 3. redux-immutable-state-invariant来检测state的不可变性。
/* const store = configureStore({
    reducer: {
        counter: counterReducer,
        peom: peomReducer
    }

}) */
//调用createStore创建store
const reducer = combineReducers({
    counter: counterReducer,
    peom: peomReducer
})
 
/*
注释该行,测试自定义的thunk函数
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk))) 
*/

const store = createStore(reducer)



/* 
不调用customizethunk函数,报错信息如下 
Actions must be plain objects. Instead, the actual type was: 'function'.
 You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions.
*/
/* 
customizethunk(store)
reduxlogger(store) 
测试自定义的applyMiddleware

*/
applyMiddlewareCustomize(store,reduxlogger,customizethunk)

export default store;

src\store\features

该文件夹创建两个不同的reducer/action

 counter.js

实时数据的reducer和action,使用createSlice

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


const counterSlice = createSlice({
    name:"counter",
    initialState:{
        number:99
    },
    reducers:{
        addNumber(state,action){
            console.log("counterSlice reducer",action.payload)
            return {...state,number:state.number+action.payload}
        },
        subNumber(state,action){
            console.log("counterSlice reducer",action.payload)
            return {...state,number:state.number-action.payload}
        }
    }
})
export const {addNumber,subNumber}=counterSlice.actions;
export default counterSlice.reducer;

poem.js

异步请求的reducer和action

import { createSlice,createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
/*  https://api.gugudata.com/text/chinesepoem/demo*/
//方式1
export const fetchPeomdataAction=createAsyncThunk("fetch/peomdata",async ()=>{
    const res= await axios.get("https://api.gugudata.com/text/chinesepoem/demo") 
   return res.data
})
//方式2
export const getPeomdataAction=createAsyncThunk("fetch/peomdata",async (extraInfo,{dispatch,getState})=>{
    //console.log('getPeomdataAction-------',extraInfo)
    const res= await axios.get("https://api.gugudata.com/text/chinesepoem/demo") 
    dispatch(changePeom(res.data.Data))
   return res.data
})
const peomSlice = createSlice({
    name:"peom",
    initialState:{
        peom:[]
    },
    reducers:{
        changePeom(state,{payload}){
           // console.log("peomSlice reducer",payload)
            state.peom=payload
        }
    },
    extraReducers:builder =>{
        //方式1
         builder.addCase(fetchPeomdataAction.pending,(state)=>{
            console.log("fetchPeomdataAction.pending")
         }).addCase(fetchPeomdataAction.fulfilled,(state,{payload})=>{
          //  console.log("fetchPeomdataAction.fulfilled",payload)
            state.peom=payload.Data
         }) 
    }
})
export const {changePeom}=peomSlice.actions;
export default peomSlice.reducer;

src\store\middleware

该文件夹实现自定的中间价

applyMiddlewareCustomize.js

自定义实现redux提供的applyMiddileware功能,applyMiddileware主要功能时执行传入的函数,每个函数的实参为store

function applyMiddlewareCustomize(store, ...fns) {
    fns.forEach((fn) => {
        fn(store)
    })

}
export default applyMiddlewareCustomize;

customizethunk.js

自定义实现redux-thunk提供的thunk,处理为function的action

function customizethunk(store) {
    //最外层的派发函数
    const next = store.dispatch
    function thunkAndDispatch(action) {
        //真正派发action的代码,使用之前真正的dispatch
    
        if (typeof action === 'function') {
            //当派发的action依然为函数时,
            console.log('------派发的action为函数', action)
            action(store.dispatch,store.getState)
        }else {
            next(action)
        }
    }
    //monkey patch:串改现有代码,对整体的执行逻辑进行修改
    store.dispatch = thunkAndDispatch
}
export default customizethunk;

 reduxlogger.js

每次派发action后,再执行dispatch前打印action,每次执行后打印state

function reduxlogger(store) {
    const next = store.dispatch
    function logAndDispatch(action) {
        console.log('当前派发的action:', action)

        //真正派发action的代码,使用之前真正的dispatch
        next(action)
        console.log('派发action结果:', store.getState())
    }
    //monkey patch:串改现有代码,对整体的执行逻辑进行修改
    store.dispatch = logAndDispatch
}
export default reduxlogger;

 index.js

将创建的中间件导出

import reduxlogger from "./reduxlogger";
import customizethunk from "./customizethunk";
import applyMiddlewareCustomize from "./applyMiddlewareCustomize";

export {
    reduxlogger,
    customizethunk,
    applyMiddlewareCustomize
}

src\views

组件

About.js

使用自定义的connect高阶函数,以及counter reducer/action

import React, { Component } from 'react'
import { connect } from '../hoc' //使用自定义的connect函数
import { addNumber, subNumber } from '../store/features/counter'
class About extends Component {
    addNumber(num) {
        this.props.addNumberMap(num)
    }
    subNumber(num) {
        this.props.subNumberMap(num)
    }
    render() {
        const { number } = this.props
        return (
            <div>
                <h3>About 计数器:{number}</h3>
                <button onClick={e => this.addNumber(5)}>+5</button>
                <button onClick={e => this.subNumber(2)}>-2</button>
            </div>
        )
    }
}
const mapStateToProps = (state) => {

    return {
        number: state.counter.number
    }

}
const mapDispatchToProps = (dispatch) => ({
    addNumberMap(num) {
        dispatch(addNumber(num))
    },
    subNumberMap(num) {
        dispatch(subNumber(num))
    }
})
export default connect(mapStateToProps, mapDispatchToProps)(About)

Home.jsx

使用react-redux提供 的connect函数,以及用axios派发function类型的action获取数据并存入store中

import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
//import axios from 'axios'
import { addNumber, subNumber } from '../store/features/counter'
import {fetchPeomdataAction,getPeomdataAction} from '../store/features/poem'
//import store from '../store'
export class Home extends PureComponent {
  componentDidMount() {
   /*  axios.get("https://api.gugudata.com/text/chinesepoem/demo").then(res => {
      const peom = res.data.Data
      store.dispatch(changePeom(peom))
      console.log(peom)
    }) */
    this.props.fetchPeomdata()
  }

  addNumber(num) {
    this.props.addNumberMap(num)
  }
  subNumber(num) {
    this.props.subNumberMap(num)
  }
  render() {
    const { number } = this.props
    //  console.log("++++++++", number)
    return (
      <div>
        <h2>Home count:{number}</h2>
        <button onClick={e => this.addNumber(5)}>+5</button>
        <button onClick={e => this.subNumber(2)}>-2</button>
      </div>
    )
  }
}
const mapStateToProps = (state) => {
  return {
    number: state.counter.number
  }
}
const mapDispatchToProps = (dispatch) => ({
  addNumberMap(num) {
    dispatch(addNumber(num))
  },
  subNumberMap(num) {
    dispatch(subNumber(num))
  },
   fetchPeomdata(){
    //方式1
    //dispatch(fetchPeomdataAction())
    //方式2 getPeomdataAction
    dispatch(getPeomdataAction({name:"test"}))
   }
})
export default connect(mapStateToProps, mapDispatchToProps)(Home);

 Profile.js

使用函数式组件,并用hooks获取state和dispatch,并将axios获取的数据在组件中显示

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { addNumber, subNumber } from '../store/features/counter';

function Profile(props) {
    const { number } = useSelector(state => state.counter)
    const { peom } = useSelector(state => state.peom)
    const dispatch = useDispatch()
    const addcounter = (num) => {
        dispatch(addNumber(num))
    }
    const subcounter = (num) => {
        dispatch(subNumber(num))
    }
    //console.log("Profile 获取诗词", peom)
    return (
        <div>
            <h2> Profile counter:{number}</h2>
            <button onClick={e => addcounter(5)}>+5</button>
            <button onClick={e => subcounter(2)}>-2</button>
            <div className='peom'>
                <h2> Profile 诗词大会</h2>
                {
                    peom?.map((item, index) => {
                        return (<div key={index}>
                            <h3>{item.Title}</h3>
                            <h4>{item.Author}</h4>
                            <h5>{item.Type}</h5>
                            <ul>
                                {item.Content.map((it, index) => {
                                    return <li key={index}>{it}</li>
                                })}
                            </ul>

                        </div>
                        )
                    })
                }
            </div>

        </div>
    );
}

export default Profile;

程序运行结果

react redux中间件使用及原理_applymiddlewa原理_02

举报

相关推荐

0 条评论