React--》redux的传统写法与新技术redux toolkit的相柔并济——万言总结文章,你能学会多少。_redux写法繁琐有什么办法解决吗(1)

Redux Toolkit 是官方推荐的编写 Redux 逻辑的方法。它围绕 Redux 核心,并包含对于构建 Redux 应用必不可少的软件包和功能。Redux Toolkit 建立在最佳实践中,简化了大多数 Redux 任务,防止了常见错误,并使编写 Redux 应用程序更加容易。但是许多老教程仍然使用过时的Redux手写模式,如果你使用老版本并且想消除删除线,可以切换 legacy_createStore 模式导出的 API,此种模式下没有@deprecation标签。

本文会先讲解一下老版本的使用,然后再简单提及一下 Redux Toolkit 的使用,如下:

在src目录下新建 redux 文件夹,里面存放着加工数据的reducer和接收加工数据的store文件夹:

// store.js文件,该文件专门用于暴露一个store对象,整个应用只有一个store对象
// 切换 legacy_createStore 模式导出的 API,此种模式下没有(警告)@deprecation标签。
import { legacy_createStore as createStore } from "redux";
// 引入处理count的reducer
import countReducer from  './countReducer.js'
// 暴露store
export default createStore(countReducer)
// countReducer.js文件,该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
// reducer函数会接到两个参数,分别是:之前的状态(preState),动作对象(action)

export default function countReducer(preState,action){
  // 从action中获取:type、data
  const {type,data} = action
  // 根据type决定如何加工数据
  switch (type) {
    case 'increment': // 如果是加
      return preState + data
    case 'decrement': // 如果是减
      return preState - data
    default:
      return 0 // 页面的初始数据
  }
}

创建好redux相关文件之后,我们直接要在使用store状态的组件中引入即可,因为redux只处理数据状态,并不会重新渲染,所以需要我们进行手动渲染设置,如下:

import React, { Component } from 'react'
// 引入store,用于获取redux中保存状态
import store from '../../redux/store.js'

export default class Count extends Component {
  componentDidMount(){
    // 检测redux中状态的变化,只要变化就调用render
    store.subscribe(()=>{
      // this.setState({}) // 什么也不传值,只调用也能重新调用render渲染页面
      this.forceUpdate() // 强制刷新
    })
  }

  increment = () =>{ 
    const {value} = this.selectNumber
    // 通过dispatch来分配任务
    store.dispatch({type:'increment',data:value*1})
  } 
  decrement = () =>{ 
    const {value} = this.selectNumber
    store.dispatch({type:'decrement',data:value*1})
  }
  incrementIfOdd = () =>{ 
    const {value} = this.selectNumber
    const count = store.getState()
    if(count%2!==0){
      store.dispatch({type:'increment',data:value*1})
    }
  }
  incrementAsync = () =>{ 
    const {value} = this.selectNumber
    setTimeout(()=>{
      store.dispatch({type:'increment',data:value*1})
    },500)
  }
  render() {
    return (
      <div style={{padding:'15px'}}>
        {/* 通过getState这个API获取数据 */}
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={c => this.selectNumber = c}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    )
  }
}

如果要使用redux状态的组件有很多,每个组件都写componentDidMount来重新渲染页面会很繁琐,推荐将监听store变化的函数写在入口文件,如下:

import React from 'react';
import ReactDOM from "react-dom";
import App from './App'
import store from './redux/store.js'

ReactDOM.render(<App />,document.getElementById('root'))

// 检测redux中状态的变化,只要变化就调用render
store.subscribe(()=>{
    ReactDOM.render(<App />,document.getElementById('root'))
})

接下来使用actionCreators来告知store要传入的动作的对象,在redux文件夹下新建count_action文件,如下:

// count_action.js文件,该文件专门为Count组件生成action对象

// 完整书写
export const createIncrementAction = (data)=>{
  return {type:'increment',data}
}
// 精简书写
export const createDecrementAction = data => ({type:'decrement',data})

将创建好的actionCreators引入要使用store的Count组件,将原本的dispatch要写的对象,替换掉

在日常开发中,如果文件的数量太多,要书写很多常量,可以在redux文件夹下新建一个文件来专门处理常量问题,放置容易写错的type值,防止程序员因为粗心而将单词写错,也便于以后的管理:

处理好常量之后,在使用type和data的对象的actionCreators和Reducer文件引入即可:

处理异步action

同步action就是我们平常在调用actionCreators时,dispatch传入的type和data对象。

什么是异步action呢?举个例子:component组件相当于顾客,actionCreators相当于服务员,store相当于老板,reducers相当于厨师,顾客去饭店点餐,告诉服务员,5分钟后再上菜,掐时间的是服务员,这个就属于异步action,当然顾客也能去饭店等五分钟再告诉服务员上菜,而这需要顾客自己掐时间,也能达到相同的效果,但是顾客为啥要掐时间呢?服务员是服务我们的,应该需要他去劳作,哈哈,大致就是这个意思,但是怎么做呢?如下:将action文件设置一个处理异步任务的函数。

// count_action.js文件,该文件专门为Count组件生成action对象
import { INCREMENT,DECREMENT } from "./constant"
import store from '../redux/store'
// 完整书写
export const createIncrementAction = (data)=>{
  return {type:INCREMENT,data}
}
// 精简书写
export const createDecrementAction = data => ({type:DECREMENT,data})
// 异步的action
export const createIncrementAsyncAction = (data,time) =>{
  // 函数柯里化,将函数的返回值暴露出来
  return () =>{
    setTimeout(()=>{
      store.dispatch(createIncrementAction(data))
    },time)
  }
}

我们在调用函数的时候,直接传入具体实参即可:

而我们在调用这个异步任务后,控制台爆出如下错误: 其告诉我们action只能接收对象不能接收函数,如果要接收函数需要使用第三方中间件,并且名字在控制台也告诉了我们:redux-thunk

npm安装完 redux-thunk之后,在store文件中引入并使用即可,如下:

// store.js文件,该文件专门用于暴露一个store对象,整个应用只有一个store对象
// 切换 legacy_createStore 模式导出的 API,此种模式下没有(警告)@deprecation标签。
import { legacy_createStore as createStore,applyMiddleware } from "redux";
// 引入处理count的reducer
import countReducer from  './countReducer.js'
// 引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'

// 暴露store
export default createStore(countReducer,applyMiddleware(thunk))

注意:异步action不是必须要写的,完全可以自己等待异步任务的结果然后再去分发同步action。当我们想要对状态进行操作,但是具体的数据靠异步任务返回的时候可以采用异步action。

react-redux的使用

facebook公司发现,许多的react开发人员都倾向于使用redux进行集中式状态管理,所以其公司自行推出了react-redux插件库,便于react和redux进行协调开发,react-redux模型图如下:

根据原理图,我们还需要创建一个容器组件,将以前直接在UI组件库里面调用redux的API进行抽离出来,所有与redux相关的API全部放置在容器组件中进行,如下:

在书写容器组件代码之前,需要npm安装一下react-redux这个包,安装完成之后,我直接给出以下代码,用容器组件来连接UI组件,如下:

// 引入Count的UI组件
import CountUI from '../../components/Count'
import { createIncrementAction,createDecrementAction,createIncrementAsyncAction } from '../../redux/count_action'


// 引入connect用于连接UI组件于redux
import { connect } from 'react-redux'

/* 
  1.mapstateToProps函数返回的是一个对象;
  2.返回的对象中的key就作为传递给UI组件props的key, value就作为传递给UIT组件props的value
  3.mapStateToProps用于传递状态
*/
function mapStateToProps(state){
  return {count:state}
}
/* 
1.mapDispatchToProps函数返回的是一个对象;
2.返回的对象中的key就作为传递给UI组件props的key, value就作为传递给UI组件props的value
3.mapDispatchToProps用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch){
  return {
    jia:(number)=>{dispatch(createIncrementAction(number))},
    jian:(number)=>{dispatch(createDecrementAction(number))},
    jiaAsync:(number,time)=>{dispatch(createIncrementAsyncAction(number,time))}
  }
}

// 使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)

当然这个容器组件也可以采用简写方式,因为react-redux这个第三方库会自动帮助我们分发任务所以我们不需要在写dispatch,因为是函数传参,函数也能直接用箭头函数写在参数上:

// 引入Count的UI组件
import CountUI from '../../components/Count'
import { createIncrementAction,createDecrementAction,createIncrementAsyncAction } from '../../redux/count_action'


// 引入connect用于连接UI组件于redux
import { connect } from 'react-redux'

/* 
  1.mapstateToProps函数返回的是一个对象;
  2.返回的对象中的key就作为传递给UI组件props的key, value就作为传递给UIT组件props的value
  3.mapStateToProps用于传递状态
*/
// function mapStateToProps(state){
//   return {count:state}
// }
// 简写
// const mapStateToProps = state => ({count:state})
/* 
1.mapDispatchToProps函数返回的是一个对象;
2.返回的对象中的key就作为传递给UI组件props的key, value就作为传递给UI组件props的value
3.mapDispatchToProps用于传递操作状态的方法
*/
// function mapDispatchToProps(dispatch){
//   return {
      // jia:(number)=>{dispatch(createIncrementAction(number))},
      // jian:(number)=>{dispatch(createDecrementAction(number))},
      // jiaAsync:(number,time)=>{dispatch(createIncrementAsyncAction(number,time))}
//   }
// }
// 简写
// const mapDispatchToProps = dispatch => ({
//   jia:(number)=>{dispatch(createIncrementAction(number))},
//   jian:(number)=>{dispatch(createDecrementAction(number))},
//   jiaAsync:(number,time)=>{dispatch(createIncrementAsyncAction(number,time))}
// })

// 使用connect()()创建并暴露一个Count的容器组件
export default connect(
  state => ({count:state}),
  // dispatch => ({
  //   jia:(number)=>{dispatch(createIncrementAction(number))},
  //   jian:(number)=>{dispatch(createDecrementAction(number))},
  //   jiaAsync:(number,time)=>{dispatch(createIncrementAsyncAction(number,time))}
  // })
  // 因为react-redux会帮助我们自动分发dispatch
  {
    jia:createIncrementAction,
    jian:createDecrementAction,
    jiaAsync:createIncrementAsyncAction
  }
)(CountUI)

因为UI组件是被容器组件给包裹住的,所有需要我们在App.jsx中引入容器组件而不是UI组件,并且需要我们在容器组件中以props的形式传入store对象,以此来在容器组件中能使用store的API:

那如果容器组件如果有很多的情况下,每个容器组件都需要props传入store未免有些太过繁琐了,这里我们可以借助 Provider 组件来一次性处理所有问题,如下:

根据以上的操作,我们在UI组件中就不需要来进行redux中store相关API的书写了,直接书写容器组件中定义的操作状态的方法即可,如下:

import React, { Component } from 'react'
export default class Count extends Component {

  increment = () =>{ 
    const {value} = this.selectNumber
    // 通过dispatch来分配任务
    this.props.jia(value*1)
  } 
  decrement = () =>{ 
    const {value} = this.selectNumber
    this.props.jian(value*1)
  }
  incrementIfOdd = () =>{ 
    const {value} = this.selectNumber
    if(this.props.count % 2 !==0){
      this.props.jia(value*1)
    }
  }
  incrementAsync = () =>{ 
    const {value} = this.selectNumber
    this.props.jiaAsync(value*1,500)
  }
  render() {
    // console.log(this.props);
    return (
      <div style={{padding:'15px'}}>
        {/* 通过getState这个API获取数据 */}
        <h1>当前求和为:{this.props.count}</h1>
        <select ref={c => this.selectNumber = c}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    )
  }
}

有了容器组件的出现,传统的需要在强制去调用render刷新页面已经不在需要了,如下:

写到这大家可能会有点疑惑,如果我们要是有20UI组件的话,是不是还需要写20个容器组件,这样不是导致我们的文件数过大吗,没错是这样,那么有什么办法来解决呢,其实我们完全可以将容器组件和UI组件整合在一起,因为react-redux并没有禁止我们将这两个组件写成一个啊,所有我们将之前的UI组件代码粘贴到容器组件中,如下:(完全没问题)

// 引入Count的UI组件
import { createIncrementAction,createDecrementAction,createIncrementAsyncAction } from '../../redux/count_action'
// 引入connect用于连接UI组件于redux
import { connect } from 'react-redux'

import React, { Component } from 'react'

class Count extends Component {

  increment = () =>{ 
    const {value} = this.selectNumber
    // 通过dispatch来分配任务
    this.props.jia(value*1)
  } 
  decrement = () =>{ 
    const {value} = this.selectNumber
    this.props.jian(value*1)
  }
  incrementIfOdd = () =>{ 
    const {value} = this.selectNumber
    if(this.props.count % 2 !==0){
      this.props.jia(value*1)
    }
  }
  incrementAsync = () =>{ 
    const {value} = this.selectNumber
    this.props.jiaAsync(value*1,500)
  }
  render() {
    // console.log(this.props);
    return (
      <div style={{padding:'15px'}}>
        {/* 通过getState这个API获取数据 */}
        <h1>当前求和为:{this.props.count}</h1>
        <select ref={c => this.selectNumber = c}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    )
  }
}

// 使用connect()()创建并暴露一个Count的容器组件
export default connect(
  state => ({count:state}),
  {
    jia:createIncrementAction,
    jian:createDecrementAction,
    jiaAsync:createIncrementAsyncAction
  }
)(Count)

当我们要进行多组件的redux集中状态管理怎么办,要知道我们之前书写store的时候,只传入一个组件的,如果要进行多组件的redux需要借助redux的一个API,combineReducers用于管理多个组件并将其封装成一个对象,如下:

// store.js文件,该文件专门用于暴露一个store对象,整个应用只有一个store对象
// 切换 legacy_createStore 模式导出的 API,此种模式下没有(警告)@deprecation标签。
import { legacy_createStore as createStore,applyMiddleware,combineReducers } from "redux";
// 引入处理count的reducer
import countReducer from  './reducers/count.js'
// 引入处理person的reducer
import personReducer from "./reducers/person.js";
// 引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'

// 将所有的容器组件合并成一个对象
const allReducer = combineReducers({
  count:countReducer,
  person:personReducer
})

// 暴露store
export default createStore(allReducer,applyMiddleware(thunk))

当我们书写多个组件进行redux的时候,还需要书写其actions和reducers文件,已经其容器组件和UI组件合并的文件:

actionCreators文件

Reducers文件

import {ADD_PERSON} from '../constant'

// 初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]

export default function personReducer(preState=initState,action){
  const {type,data} = action
  switch (type){
    case ADD_PERSON:
      return [data,...preState]
    default:
      return preState
  }
}

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

ction personReducer(preState=initState,action){
const {type,data} = action
switch (type){
case ADD_PERSON:
return [data,…preState]
default:
return preState
}
}




[外链图片转存中...(img-QsWYM6CC-1714246909214)]
[外链图片转存中...(img-rgIT0rZc-1714246909215)]
[外链图片转存中...(img-pWcZWuov-1714246909215)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值