一文学会react+redux(模块化/同步/异步操作)

本文基于npx create-react-app创建

      • 太久没看react,闲来无事重新捡起做一点笔记,希望对部分vue的同行想学习redux起到一些帮助
    • 1.准备工作安装
      • 1.安装项目插件
      • 2.修改`package.json`中的scripts,将`react-scripts`替换为`craco`:
      • 3.craco.config.js
        • 根目录下创建或修改`craco.config.js`来配置Less 以及@别名配置:
    • 2.redux基础操作-同步
      • 2.1.准备工作
          • 2.1.1.src下创建view
          • 2.1.2.view下创建两个文件夹(Home,About)
          • 2.1.3.Home和About下创建Home.jsx/About.jsx
          • 2.1.4.src下创建store文件夹
          • 2.1.5.store下创建reducer1.js 用于单个模块化的reducer
          • 2.1.6.store下创建reducer.js 用于合并reducer
          • 2.1.7.store下创建store.js store的暴露文件
      • 2.2 编写reducer1.js文件
      • 2.3 reducer.js 文件编写
      • 2.4 store.js操作创建store
      • 2.5.页面中的基础使用-同步(1)
        • 2.5.1 Home.jsx store.dispatch使用1
        • 2.5.2 Home.jsx Home.jsx store.dispatch使用2
            • 2.5.2.1 修改reducer1.js action.payload属性(携带的信息)
            • 2.5.2.2 action携带信息payload
      • 2.6 页面中的基础使用-同步(2)-高阶组件
        • 2.6.1 需要修改index.js
        • 2.6.2 修改Home.jsx文件
    • 3.redux异步操作
      • 1.安装中间件扩展dispatch功能
      • 2.原来的dispatch调用是这样的
      • 3.模块化actions,编写模拟异步请求
        • 3.1 store文件夹下创建actions文件夹
        • 3.2 actions文件夹下创建独立的action模块 action1.js
        • 3.3 constant.js文件 新增异步action.type
        • 3.4 action1.js编写如下
        • 3.5 reducer1.js 新增action匹配
        • 3.6 Home.jsx修改如下 新增了incrementAsync函数 点击按钮执行
      • 4.hook useSelector useDispatch

太久没看react,闲来无事重新捡起做一点笔记,希望对部分vue的同行想学习redux起到一些帮助

1.准备工作安装

1.安装项目插件

yarn add @craco/craco craco-less

2.修改package.json中的scripts,将react-scripts替换为craco

"scripts": {
  "start": "craco start",
  "build": "craco build",
  "test": "craco test",
  // ...
}

3.craco.config.js

根目录下创建或修改craco.config.js来配置Less 以及@别名配置:
const CracoLessPlugin = require('craco-less');
const path = require('path');

module.exports = {
  plugins: [
    // 配置CracoLessPlugin插件,用于处理Less样式文件
    {
      plugin: CracoLessPlugin, // 引入的插件
      options: {
        lessLoaderOptions: {
          // 配置Less加载器
          lessOptions: {
            modifyVars: { '@primary-color': '#1DA57A' }, // 自定义Less变量,例如改变主题颜色
            javascriptEnabled: true, // 启用Less中的JavaScript
          },
        },
      },
    },
  ],
  webpack: {
    configure: (webpackConfig) => {
      webpackConfig.resolve.alias = {
        ...(webpackConfig.resolve.alias || {}),
        '@': path.resolve(__dirname, 'src'),
      };
      return webpackConfig;
    },
  },
};

2.redux基础操作-同步

2.1.准备工作

2.1.1.src下创建view
2.1.2.view下创建两个文件夹(Home,About)
2.1.3.Home和About下创建Home.jsx/About.jsx
2.1.4.src下创建store文件夹
2.1.5.store下创建reducer1.js 用于单个模块化的reducer
2.1.6.store下创建reducer.js 用于合并reducer
2.1.7.store下创建store.js store的暴露文件
-src
	-view
		-Home
			Home.jsx
		-About
			About.jsx
	-store
		-reducer1.js
		-reducer.js
		-store.js
		

2.2 编写reducer1.js文件

//初始化该模块的state
const initalState = 0
//reducer接受两个参数 state(初始值initalState) action:一个对象{type:"自定义的类型INCREMENT",payload:"携带的信息"}
const counterReducer=(state=initalState,action)=>{
    //使用swicth判断匹配类型 return 对应的操作结果
    Switch(action.type){
        case "INCREMENT":
        	//注意这里不是直接改变state 而是返回对state操作的结果
        	return state+1;
        default :
        	return state
    }
}
//将counterReducer进行暴露 在reducer.js中引入进行合并操作
export default counterReducer

2.3 reducer.js 文件编写

//从redux引入合并函数
import {combineReducers} from "redux"
//引入reducer1.js中的模块化的reducer函数
import counterReducer from "./reducer1"
const reducer = combineReducers({
	//state name:reducer name 
    //这里代表这个加载进去的模块名就是counter
    //后续使用store.getState().counter可获取该模块的state 
    //connect connect(mapStateToProps,mapDispatchToProps)(Home) 
    //使用connect时 mapStateToProps(state) { return { counter: state.counter } }
	counter:counterReducer
})

//暴露合并的reducer
export default reducer

2.4 store.js操作创建store

//从redux引入创建函数
import {createStore} from "redux"
//从reducer.js引入合并后的reducer
import rootReducer from "./reducer"

const store = createStore(rootReducer)
//暴露store
export default store

2.5.页面中的基础使用-同步(1)

2.5.1 Home.jsx store.dispatch使用1
//引入写好的store.js
import store from "@/store/store"
//从react-redux引入useSelect函数 用户获取store 中对应的state
import {useSelector} from "react-redux"
function Home(){
    const counter = useSelector(state=>state.counter)
    const increment = ()=>{
        store.dispatch({type:"INCREMENT"})
    }
	return (
		<>
        	
Home
reducer1中的counter为:{counter}
{/*在页面上点击按钮后,counter会变化(每次+1)*/} ) } export default Home
2.5.2 Home.jsx Home.jsx store.dispatch使用2
2.5.2.1 修改reducer1.js action.payload属性(携带的信息)
//初始化该模块的state
const initalState = 0
//reducer接受两个参数 state(初始值initalState) action:一个对象{type:"自定义的类型INCREMENT",payload:"携带的信息"}
const counterReducer=(state=initalState,action)=>{
    //使用swicth判断匹配类型 return 对应的操作结果
    Switch(action.type){
        case "INCREMENT":
        	//注意这里不是直接改变state 而是返回对state操作的结果
        	//这里使用action的pyload属性(也就是携带的信息)
        	return state+action.payload;
        default :
        	return state
    }
}
//将counterReducer进行暴露 在reducer.js中引入进行合并操作
export default countReducer
2.5.2.2 action携带信息payload
//引入写好的store.js
import store from "@/store/store"
//从react-redux引入useSelect函数 用户获取store 中对应的state
import {useSelect} from "react-redux"
function Home(){
    const counter = useSelect(state=>state.counter)
    const increment = ()=>{
        //增加payload
        store.dispatch({type:"INCREMENT",payload:5})
    }
	return (
		<>
        	
Home
reducer1中的counter为:{counter}
{/*在页面上点击按钮后,counter会变化(每次+5)*/} ) } export default Home

2.6 页面中的基础使用-同步(2)-高阶组件

2.6.1 需要修改index.js
//引入Provider
//从react-redux中引入 用户连接redux和react的插件
import {Provider} from "react-redux"
//引入store/store.js
import store from "@/store/store"
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
	//使用Provider包裹App组件
  
    
  
);

我的App组件是这样的:

import { BrowserRouter, Routes, Route,Navigate } from "react-router-dom"
import Home from "@/view/Home/Home";
import About from "@/view/About/About"
import NotFound from "@/view/NotFound/NotFound"
function App() {
  return (
    
      
        }>
        }>
        }>
        } /> {/* 使用'*'来匹配所有未找到的路由 */}
      
    
  );
}
export default App;

2.6.2 修改Home.jsx文件
//从react-redux引入connect
import {connect} from "react-redux"
const mapStateToProps=(state)=>{
	return {
        counter:state.counter
    }
}
const mapDispatchToProps=(dispatch)=>{
    return{
        increment:()=>{
            dispatch({type:"INCREMENT",payload:5})
        }
    }
}
function Home(props){
    //使用connect包裹的操作相当于将state和increment映射进了Home组件的props
    const {counter,increment} = props
	return (
    <>
        
Home
reducer1中的counter为:{counter}
{/*在页面上点击按钮后,counter会变化(每次+5)*/} ) } //使用connect参入参数回调函数mapStateToProps,mapDispatchToProps包裹Home组件后暴露 高阶函数用法 export default connect(mapStateToProps,mapDispatchToProps)(Home)

该使用效果与同步(1)一样

3.redux异步操作

1.安装中间件扩展dispatch功能

dispatch接受的是一个对象

也就是说redux分发任务的时候disptach接受的action是一个对象,当使用异步操作时,可以利用thunk中间件,让dispatch接受一个函数

安装redux-thunk插件

yarn add redux-thunk

2024-07-11补充:store需要导入applyMiddleware 和thunk

import {createStore,applyMiddleware } from "redux"
import thunk from "redux-thunk"
import rootReducer from "./reducer"
//创建Store 新增applyMiddleware(thunk)
const store = createStore(rootReducer,applyMiddleware(thunk))

export default store

2.原来的dispatch调用是这样的

const mapDispatchToProps=(dispatch)=>{
    return{
        increment:()=>{
            //dispatch接受的是一个对象
            dispatch({type:"INCREMENT",payload:5})
        }
    }
}

另一中接受对象的写法(等同于上面):

function incrementCounter(amount){
	return {type:"INCREMENT",payload:amount}
}
const mapDispatchToProps=(dispatch)=>{
    return{
    	//传入回调函数 回调函数会返回对象{type:"INCREMENT",payload:5}
        increment:dispatch(incrementCounter(5))
    }
}

另外,可将incrementCounter函数放入一个单独创建的actions文件夹下创建的action.js文件,达成模块化的操作

3.模块化actions,编写模拟异步请求

3.1 store文件夹下创建actions文件夹
3.2 actions文件夹下创建独立的action模块 action1.js

这里同理,可以将reducer.js和reducer1.js放入单独的reducer文件夹

-store
	-actions
		-action1.js
	-reducer.js
	-reducer1.js
	-store.js
	//新增一个常亮文件 用于放置action.type常量 可将之前的reducer Switch中的case 后的常亮提取出来放到此处,再从此处暴露引入到reducer文件中
	-constant.js
3.3 constant.js文件 新增异步action.type
export const TEST_INCREMENT = "TEST_INCREMENT"
3.4 action1.js编写如下
//引入需要用的action.type
import { TEST_INCREMENT } from "@/store/constant"
//doSome 是一个模拟请求的函数 这里doSome函数可以提取到对应的api请求文件中
function doSome() {
    return new Promise(r => {
        setTimeout(() => {
            r({ code: 0, data: 10 })
        }, 2000)
    })
}
//fetchTest是一个函数,它返回一个异步函数 函数中执行模拟请求 dispatch分发action 去执行对应的reducer操作
const fetchTest=()=>{//注意这里没有async 这里就是一个普通函数
    //回调函数接受传递dispatch
	return async(dispatch)=>{//这里返回的才是异步函数
        let res = await doSome()
        if(res.code===0){
            //调用dispatch 传入action对象  data等于10 也就是说2s请求完成后,state会增加10 参考3.4 reducer1.js 新增action匹配
            dispatch({type:TEST_INCREMENT,payload:res.data})
        }
    }
}
3.5 reducer1.js 新增action匹配
//从constant.js引入需要使用的action.type常量
import { TEST_INCREMENT } from "@/store/constant"
const initalState = 0
const counterReducer = (state = initalState, action) => {
    switch (action.type) {
        case "INCREMENT":
            return state + action.payload;
        //这里就是例子,直接使用引入的常量进行匹配
        case TEST_INCREMENT:
            return state + action.payload;
        default:
            return state;
    }
}
export default counterReducer
3.6 Home.jsx修改如下 新增了incrementAsync函数 点击按钮执行
import React from 'react'
import { useNavigate } from "react-router-dom"
import { fetchTest} from "../../store1/actions1"
import { connect } from "react-redux"

function Home(props) {
    const history = useNavigate()
    const goToAbout = () => {
        history("/about")
    }
    //从props中解构出来 connect映射的数据(状态以及函数)
    const { counter, increment, incrementAsync } = props
    return (
        <div>
            <header>Home</header>
            <section>
                <div></div>
                <button onClick={increment}>修改state.counter{counter}</button>
                <button onClick={incrementAsync}>异步改变counter{counter}</button>
                <button onClick={goToAbout}>去about页面</button>
            </section>

        </div>
    )
}
const mapStateToProps = (state) => {
    return {
        counter: state.counter
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        increment: () => {
            dispatch({ type: "INCREMENT", payload: 5 })
        },
        //这里 incrementAsync是一个函数 函数内部调用dispatch dispatch的参数就是fetchTest(),fetchTest()返回的是一个异步函数,这个异步函数接受dispatch,等待请求结果,然后使用dispatch分发对应的action 参考3.3 action1.js编写如下
        incrementAsync:()=>dispatch(fetchTest())
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Home)

以上都是基于connect 高阶组件映射props 调用dispatch达到操作store的效果

此外还可以使用hook useSelect useDispatch达到操作store的效果

4.hook useSelector useDispatch

其他文件不变,Home.jsx使用hook操作

使用方法如下:

import React from 'react'
import { useNavigate } from "react-router-dom"
import { fetchUser} from "../../store1/actions1"
// import { connect } from "react-redux"
import {useSelector,useDispatch} from "react-redux"

export default function Home() {
    const history = useNavigate()
    const goToAbout = () => {
        history("/about")
    }
    //使用useSelector获取对应的仓库状态
    const counter = useSelector(state => state.counter)
    //useDispatch获取dispatch
    const dispatch = useDispatch()
    const increment=()=>{
        //dispatch分发action
        dispatch({ type: "INCREMENT",payload:5 })
    }
    //dispatch 执行异步操作
    const incrementAsync=()=>dispatch(fetchUser())
    return (
        
Home
) }

你可能感兴趣的:(web前端,react.js,前端,javascript)