09_Redux的应用

文章详细介绍了Redux的状态管理框架,包括其基本概念、应用实例(如求和案例的纯React版本和Redux版本)、react-redux的使用,以及如何优化容器组件和UI组件的交互,强调了Redux在处理状态变化和页面更新中的作用,并提供了使用react-redux连接组件和store的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、什么是redux?

二、redux的应用

2.1 求和案例

2.1.1 纯react版本

2.1.2  redux版本

(1)测试版本

(2)正式版本

三、react-redux

3.1 react-redux的使用

1. UI组件:compoents,不能使用任何redux的api,只负责页面的呈现和交互等

2. 容器组件:container,负责和redux通信,数据获取、状态更新,将结果交给UI组件

3.2 案例的优化

3.2.1  mapDispatchToProps的简化

3.2.1  页面重新渲染问题

3.2.3 容器组件的store

3.2.4  整合UI组件和容器组件


一、什么是redux?

redux是一个专门用于做状态管理的JS库(不是react插件库)

redux独立所有组件存在,集中管理react应用中多个组件共享的状态

二、redux的应用

2.1 求和案例

2.1.1 纯react版本

  • Count组件
import React, { Component } from "react"
import "./index.css"
export default class Count extends Component {
  state = { count: 0 }
  addNum = () => {
    const { value } = this.selectNumber
    const { count } = this.state
    this.setState({ count: count + value * 1 })
  }
  //减法
  subNum = () => {
    const { value } = this.selectNumber
    const { count } = this.state
    this.setState({ count: count - value * 1 })
  }
  //奇数加
  addNumIfodd = () => {
    const { count } = this.state
    if (count % 2 !== 0) {
      this.addNum()
    }
  }
  //异步加
  addNumAsync = () => {
    setTimeout(() => this.addNum(), 1000)
  }
  render() {
    const { count } = this.state
    return (
      <div>
        <h3>
          当前求和为:<span>{count}</span>
        </h3>
        <select
          name="select"
          id=""
          ref={(c) => (this.selectNumber = c)}
        >
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>

        <button onClick={this.addNum}>+</button>
        <button onClick={this.subNum}>-</button>
        <button onClick={this.addNumIfodd}>当前求和为奇数加</button>
        <button onClick={this.addNumAsync}>异步加</button>
      </div>
    )
  }
}
h3 {
  margin: 10px 20px;
}
select {
  margin: 0 20px;
  height: 30px;
  width: 100px;
}
button {
  font-size: 16px;
  margin: 0 10px;
  padding: 5px 10px;
}

2.1.2  redux版本

npm i redux

(1)测试版本

  • store.js

        该文件专门用于暴露一个store对象,整个应用只要一个store对象

  • 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore } from "redux"

//引入为Count组件服务的reducer
import countReducer from "./count_reducer"
export default createStore(countReducer)

注意:'createStore' is deprecated,createStore被弃用,改用configureStore

configureStore写法

1. 下载依赖

npm install @reduxjs/toolkit

2. configureStore具体写法

import countReducer from "./count_reducer"
import { configureStore } from "@reduxjs/toolkit"
export default configureStore({
  reducer: countReducer,
})
  • count_reducer.js
  1. 该文件时用于创建一个为Count组件服务的reducer,reducer的本质是一个函数
  2. reducer会接到两个参数,分别为:之前的状态 preState动作对象 action
  3. reducer用于初始化状态和加工状态
  4. reducer第一次被调用时store自动触发
    1. 传递的preState:undefined
    2. 传递的action:@@REDUX/INIT
const initState = 0
export default function countReducer(preState = initState, action) {
  // console.log(preState)
  //判断初始化 if (preState === undefined) preState = 0
  //从action对象中获取type,data
  const { type, data } = action
  //根据type决定如何加工数据
  switch (type) {
    case "add":
      // console.log("add")
      return preState + data
    case "sub":
      return preState - data
    default:
      return preState
  }
}
  •  Count组件

Action

  1. 描述对 store 数据的更改操作,把数据从应用传到store。
  2. 它必须包含一个 type 字段,必须与reducer中的type对应。
  3. dispatch将action发送到reducer ,然后reducer函数根据匹配规则进状态更新
  • 加法
addNum = () => {
    const { value } = this.selectNumber
    //通知redux + value, dispatch分发
    store.dispatch({ type: "add", data: value * 1 })
 
  }
  •  奇数加
addNumIfodd = () => {
    //如何获取count?
    const count = store.getState()
    if (count % 2 !== 0) {
      this.addNum()
    }
  }
  • 页面获取count 

问题:rudux只更改状态,页面并未发生更新,所以需要手动

方法一

在Count组件的componentDidMount,通过subscribe订阅,检测redux中状态的变化,只要变化就调用render。但不能直接调用render,通过 setState 之后必调用 render。

componentDidMount() {
    store.subscribe(() => {
      //console.log("@")
      this.setState({})
    })
  }

方法二

在入口文件index.js,检测store中状态的改变,根据Reaact的DOM的Diffing算法,只会局部更新

import store from "./redux/store"
store.subscribe(() => {
  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  )
})
  •  Count组件完整代码
import React, { Component } from "react"
import "./index.css"
//用于获取store中的状态
import store from "../../redux/store"
export default class Count extends Component {
  addNum = () => {
    const { value } = this.selectNumber
    //  通知redux加value,dispatch分发
    store.dispatch({ type: "add", data: value * 1 })
    //并未发生更新,rudux只更改状态,不更新页面
  }
  //减法
  subNum = () => {
    const { value } = this.selectNumber
    store.dispatch({ type: "sub", data: value * 1 })
  }
  //奇数加
  addNumIfodd = () => {
    //如何获取count
    const count = store.getState()
    if (count % 2 !== 0) {
      this.addNum()
    }
  }
  //异步加
  addNumAsync = () => {
    setTimeout(() => this.addNum(), 500)
  }
  render() {
    return (
      <div>
        <h3>
          当前求和为:<span>{store.getState()}</span>
        </h3>
        <select
          name="select"
          id=""
          ref={(c) => (this.selectNumber = c)}
        >
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>

        <button onClick={this.addNum}>+</button>
        <button onClick={this.subNum}>-</button>
        <button onClick={this.addNumIfodd}>当前求和为奇数加</button>
        <button onClick={this.addNumAsync}>异步加</button>
      </div>
    )
  }
}

(2)正式版本

  • constant.js

        该模块式用于定义type类型的常量值,便于管理,防止单词拼写错误。将所有需要的type进行修改。

export const ADD = "add"
export const SUB = "sub"
  • count_action.js

        该文件为Count组件创建action对象

import { ADD, SUB } from "./constant"
export const addAction = (data) => ({
  type: ADD,
  data,
})
export const subAction = (data) => ({
  type: SUB,
  data,
})
  • Count组件
//引入actionCreator,用于创建action对象
import { addAction, subAction } from "../../redux/count_action"
  • 加法
addNum = () => {
    const { value } = this.selectNumber
    store.dispatch(addAction(value * 1))
  }

补充:同步Action/异步Action

1. Object{},一般对象---同步Action

2. function{},函数类型---异步Action

  • 异步Action,也就是返回值为函数,且能开启异步任务
  • 延迟的动作不想交给组件自身,想交给Action完成。想要对状态做出改变,但具体数据靠异步完成。
  • 异步Action一般都会调用同步Action
  • 异步Action不是必须要用的
export const addAsyncAction = (data, time) => {
  return (dispatch) => {
    //异步任务
    setTimeout(() => {
      dispatch(addAction(data))
    }, time)
  }
}
//异步加
  addNumAsync = () => {
    const { value } = this.selectNumber
    store.dispatch(addAsyncAction(value * 1, 500))
  }

注意:

报错内容: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.

解决方案一

npm install redux-thunk
  • store.js
import { createStore, applyMiddleware } from "redux"
import thunk from "redux-thunk"
//引入为Count组件服务的reducer
import countReducer from "./count_reducer"
export default createStore(countReducer, applyMiddleware(thunk))

解决方案二

用configureStore来创建store对象不会发生这种问题

三、react-redux

3.1 react-redux的使用

npm i react-redux

1. UI组件:compoents,不能使用任何redux的api,只负责页面的呈现和交互等

  • 通过props获取数据

2. 容器组件:container,负责和redux通信,数据获取、状态更新,将结果交给UI组件

  • 靠react-redux的connect函数生成的,​​​​使用connect()()创建并暴露一个容器组件(CountContainer)
  • connect(mapStateToProps, mapDispatchToProps)(UI组件),connect调用的函数返回值依然是函数
  • connect第一次传递两个参数,必须是函数,mapDispatchToProps也可为对象
    • mapStateToProps:映射状态,返回值必须是对象
    • mapDispatchToProps:映射操作状态的方法,返回值必须是对象
    • 返回对象中的key 作为UI组件 props的 key,value作为UI组件的props的 value
  • 容器组件的store是靠props传进入的不是直接引入

CountUI组件---页面结构

import React, { Component } from "react"
import "./index.css"

export default class Count extends Component {
  render() {
    // console.log(this.props)
    const { count } = this.props
    return (
      <div>
        <h3>
          当前求和为:<span>{count}</span>
        </h3>
        <select
          name="select"
          id=""
          ref={(c) => (this.selectNumber = c)}
        >
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>

        <button onClick={this.addNum}>+</button>
        <button onClick={this.subNum}>-</button>
        <button onClick={this.addNumIfodd}>当前求和为奇数加</button>
        <button onClick={this.addNumAsync}>异步加</button>
      </div>
    )
  }
}

Count容器组件

1.  引入UI组件

import CountUI from "../../components/Count"

2.  引入connect用于连接UI组件和redux的store

import { connect } from "react-redux"

3.  手动生成Count容器组件

export default connect(mapStateToProps, mapDispatchToProps)(CountUI)

4.  mapStateToProps: 读取 state,把当前 Redux store state 映射到UI组件的 props 中 

function mapStateToProps(state) {
  return {
    count: state,
  }
}

5.  mapDispatchToProps:将 操作状态的方法 映射到 UI组件 props中

//引入action
import { addAction, subAction, addAsyncAction } from "../../redux/count_action"
function mapDispatchToProps(dispatch) {
  return {
    add: (data) => {
      dispatch(addAction(data))
    },
    sub: (data) => {
      dispatch(subAction(data))
    },
    addAsync: (data, time) => {
      dispatch(addAsyncAction(data, time))
    },
  }
}

App.jsx --- 容器组件传store到props

import React, { Component } from "react"
import Count from "./containers/Count"
//必须这样传store,通过props传
import store from "./redux/store.js"
export default class App extends Component {
  render() {
    return (
      <div>
        <Count store={store} />
      </div>
    )
  }
}

CountUI组件--- 获取数据,展示页面

addNum = () => {
    const { value } = this.selectNumber
    this.props.add(value * 1)
  }
  //减法
  subNum = () => {
    const { value } = this.selectNumber
    this.props.sub(value * 1)
  }
  //奇数加
  addNumIfodd = () => {
    //如何获取count
    const { count } = this.props
    if (count % 2 !== 0) {
      this.addNum()
    }
  }
  //异步加
  addNumAsync = () => {
    const { value } = this.selectNumber
    this.props.addAsync(value * 1, 500)
  }

3.2 案例的优化

3.2.1  mapDispatchToProps的简化

mapDispatchToProps值为两种形式,函数或对象。对象形式可以简化代码

export default connect(
  (state) => ({ count: state }),
  // mapDispatchToProps简写  react-dedux帮自动分发
  {
    add: addAction,
    sub: subAction,
    addAsync: addAsyncAction,
  }
)(CountUI)

3.2.1  页面重新渲染问题

connect()()容器组件默认拥有检测rudeux状态改变的能力,所以不用手动重新渲染。

入口文件index.js中检测store状态重新渲染方法可删除。

3.2.3 容器组件的store

因为容器组件将来会有很多,所有不可能一一通过props传递store。我们可在入口文件将 store交由Provider保管,批量给整个应用中传递store对象,只需要写一次。

import store from "./redux/store"
import { Provider } from "react-redux"
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
)

3.2.4  整合UI组件和容器组件

把CountUI组件放到Count容器组件中,删除多余的UI组件

import React, { Component } from "react"
import "./index.css"
//引入action
import { addAction, subAction, addAsyncAction } from "../../redux/count_action"
//引入connect用于连接UI组件和redux store
import { connect } from "react-redux"

class CountUI extends Component {
  addNum = () => {
    const { value } = this.selectNumber
    this.props.add(value * 1)
  }
  //减法
  subNum = () => {
    const { value } = this.selectNumber
    this.props.sub(value * 1)
  }
  //奇数加
  addNumIfodd = () => {
    //如何获取count
    const { count } = this.props
    if (count % 2 !== 0) {
      this.addNum()
    }
  }
  //异步加
  addNumAsync = () => {
    const { value } = this.selectNumber
    this.props.addAsync(value * 1, 500)
  }
  render() {
    // console.log(this.props)
    const { count } = this.props
    return (
      <div>
        <h3>
          当前求和为:<span>{count}</span>
        </h3>
        <select
          name="select"
          id=""
          ref={(c) => (this.selectNumber = c)}
        >
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>

        <button onClick={this.addNum}>+</button>
        <button onClick={this.subNum}>-</button>
        <button onClick={this.addNumIfodd}>当前求和为奇数加</button>
        <button onClick={this.addNumAsync}>异步加</button>
      </div>
    )
  }
}

// 核心 connect(映射状态)(映射操作状态的方法)(关联组件)
export default connect(
  (state) => ({ count: state }),
  // mapDispatchToProps简写  react-dedux帮自动分发
  {
    add: addAction,
    sub: subAction,
    addAsync: addAsyncAction,
  }
)(CountUI)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

才不吃胡萝卜嘞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值