【React】第十部分 redux和react-redux

【React】第十部分 redux和react-redux



10. redux

学习文档

  1. 英文文档
  2. 中文文档
  3. Github

10.1 什么是redux?

  1. redux是一个专门用于做状态管理的JS库(不是react插件库,不是官方的插件)。

  2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用

  3. 作用: 集中式管理react应用中多个组件共享的状态

10.2 redux的工作流程图

在这里插入图片描述

在这里插入图片描述

10.3 redux三个核心概念

10.3.1 action

  1. action : 动作的对象

  2. 有两个属性

    • type : 标识属性, 值为字符串, 唯一, 必不可少
    • data : 数据属性,值类型任意,可选
  3. 举个例子:{type:'add',data:3}

10.3.2 reducer

  1. 用于初始化状态、加工状态

  2. 加工时,根据旧的state和action, 产生新的state的纯函数

10.3.3 store(核心)

  1. 将state、action、reducer联系在一起的对象

10.4 redux基本使用(完整写法)

// 完整的写法
1. npm install --save redux 安装redux

2. 在src下创建
			-- redux
				-- store.js
				-- xxx_reducer.js
				-- xxx_action.js


3. 在store.js
		- 通过去引入redux中的createStore(),创建一个store
	 	- createStore()在创建的时候需要传入一个指定服务的reducer
		- 最后暴露store
示例:
	// legacy_createStore是用于创建redux中最为核心的store
	import {legacy_createStore as createStore} from "redux"
	// 导入为xxx组件服务的reducer
	import xxxReducer from "./xxx_reducer"
	// 暴露store
	export default createStore(xxxReducer)


4.在xxx_reducer.js
	- reducer本质是一个函数,因为上述的操作导致它可以接收到两个参数previousState(之前的状态)和action(动作对象),在这里做一些操作然后返回加工后的状态
	- reducer有两个作用:初始化状态和加工状态
  - reducer第一次调用的时候,是store自动触发的,传入的previousState为undefined

示例:
let initState = 0
export default function countReducer(previousState = initState,action){
    // 在action对象中可以获取两个参数,type和data
    const {type,data} = action
    switch (type) {
        case 'plus':
            return previousState + data*1
        case 'subtract':
            return previousState - data*1
        default:
            // 初始化
            return previousState
    }
}


5. 在xxx_action
	这个文件是专门为xxx组件生成action对象
	示例:
	export const plusAction = data => ({type:'plus',data})
	export const subtractAction = data => ({type:'subtract',data})
  
  
6. 创建一个文件constant.js,该文件是用来去定义action对象中type类型的常量,目的是为了避免程序员写错,便于管理
export const PLUS = 'plus'
export const SUBSCRIBE = 'subscribe'
  
  
// 上述工作完成后,redux文件就写好了
// 下面讲述在组件中怎么使用
7. 这里我创建一个count组件
下面示例中用到几个API,解释一下它们的作用
`store.getState()` 可以获取加工完后的状态
`store.dispatch(action)` 派发一个action给store,store在交给reducer加工
`store.subscribe(()=>{})` 可以监视redux状态的变化,如果发生变化就调用该函数

示例:
import React, { Component } from 'react'
// 引入store
import store from "../../redux/store"
// 引入action
import {plusAction,subtractAction} from "../../redux/count_action"
export default class Count extends Component {
    render() {
        return (
            <div>
                {/* 调用store上的getState()获取可以获取加工完后的状态 */}
                <h1>当前的值为: {store.getState()}</h1>
                <select ref={c=>this.selectVal = c}>
                    <option value="1"> 1 </option>
                    <option value="2"> 2 </option>
                    <option value="3"> 3 </option>
                    <option value="4"> 4 </option>
                </select>
                <button onClick={this.plus}> + </button> &nbsp;
                <button onClick={this.subtract}> - </button> &nbsp;
                <button onClick={this.oddPlus}> 奇数+ </button> &nbsp;
                <button onClick={this.delayPlus}> 延迟+ </button> &nbsp;
            </div>
        )
    }

    componentDidMount(){
        // subscribe去监视redux的状态是否发生变化,只要一发生变化就调用render()
        store.subscribe(()=>{
            // 可以利用setState,更新完状态后调用render()
            this.setState({})
        })
    }

    plus = () => {    
        // 去派发一个action
        store.dispatch(plusAction(this.selectVal.value*1))      
    }
    subtract = () => {
        // 去派发一个action
        store.dispatch(subtractAction(this.selectVal.value*1))
    }
    oddPlus = () => {
        if(store.getState() % 2 !== 0){
            // 去派发一个action
            store.dispatch(plusAction(this.selectVal.value*1))
        }
    }
    delayPlus = () => {
        setTimeout(() => {
            store.dispatch(plusAction(this.selectVal.value*1))
        }, 500);
    }
}

10.5 异步action

什么时候使用异步action呢?

当想要对状态进行修改的时候,但是具体的数据是靠异步任务返回的

同步action : action是一个对象

异步action : action是一个函数

import store from "./store"
// 返回的是一个对象,所以它是同步的action
export const subtractAction = data => ({type:SUBSCRIBE,data})
// 返回的是一个函数,并且用到了异步任务,所以它是异步的action
// 返回的函数由store帮忙调用
export const addAsyncAction = (data,time) =>{
    return ()=>{
        setTimeout(() => {
            store.dispatch(plusAction(data))
        }, time);
    }
}

想要使用异步action,需要借助中间件

npm install redux-thunk
在store.js文件下
// legacy_createStore是用于创建redux中最为核心的store
// applyMiddleware用来支持使用中间件
import {legacy_createStore as createStore,applyMiddleware} from "redux"
// 导入为Count服务的reducer
import countReducer from "./count_reducer"
// 引入redux-thunk,用来支持使用异步action
import thunk from "redux-thunk"
// 暴露store
// 第一个参数用来接收reducer,第二个参数用来支持使用中间件
export default createStore(countReducer,applyMiddleware(thunk))

10.6 react-redux

10.6.1 react-redux的理解

要和redux区分开来, react-redux 一个react插件库

作用: 专门用来简化react应用中使用redux

10.6.2 react-redux将所有组件分成两大类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bMXyZjBA-1660203633424)(C:\Users\hp\Desktop\Note\图片\react-redux.png)]

这里需要明确三个点:

  1. UI组件:不能使用任何的redux的api,它只负责页面的呈现、交互等
  2. 容器组件:负责和redux通信,将结果交给UI组件,UI组件通过props接收

10.6.3 react-redux 的基本使用

1. npm install react-redux 安装react-redux

2. 在src下创建一个containers文件,用于存放容器组件

3. 引入react-redux中connect这个方法,创建一个容器组件并且和UI组件建立联系
	connect(mapStateToProps,mapDispatchToProps)(UI组件)
	- mapStateToProps(state):映射状态,返回的是一个对象
	- mapDispatchToProps(dispatch):映射的是操作对象的方法,返回的是一个对象

示例:UI组件和容器组件写一起,可以减少创建多个文件
import React, { Component } from 'react'
// 引入react-rudex中的connect
import {connect} from "react-redux"
// 引入action
import {plusAction} from "../../redux/count_action"
// 引入store
import store from "../../redux/store"
// UI组件
class Count extends Component {
    render() {
        return (
            <div>
                {/* 调用store上的getState()获取修改后的值 */}
                <h1>当前的值为: {this.props.count}</h1>
                <select ref={c=>this.selectVal = c}>
                    <option value="1"> 1 </option>
                    <option value="2"> 2 </option>
                    <option value="3"> 3 </option>
                    <option value="4"> 4 </option>
                </select>
                <button onClick={this.plus}> + </button> &nbsp;
            </div>
        )
    }
    
    plus = () => {    
        this.props.plus(this.selectVal.value) 
    }
}


const mapStateToProps = (state) =>{
   	/* 
        可以拿到总的state
        mapStateToProps函数返回的是一个对象:
        返回对象中key就作为传递UI组件props的key
        返回对象中的value就作为传递UI组件props的value
        作用:用于给UI组件传递状态
     */
    return {count:state}
}
const mapDispatchToProps = (dispatch) =>{
  	/* 
       	mapDispatchToProps函数返回的是一个对象
        返回对象中key就作为传递UI组件props的key
        返回对象中的value就作为传递UI组件props的value
        作用:用于给UI组件传递操作状态的方法
     */
    return {
        plus:(data)=>{dispatch(plusAction(data))}
    }
}

// 创建容器组件并且暴露
export default connect(mapStateToProps,mapDispatchToProps)(Count)


4.使用容器组件需要传入store,用于和redux建立联系
<容器组件 store={store}/>

当使用了connect方法后,它具备了监视redux的能力,所以不需要再写store.subscribe()去监视redux状态的变化

下面代码就可以不用再写了

    componentDidMount(){ 
      // subscribe去监视redux的状态是否发生变化,只要一发生变化就调用render()
        store.subscribe(()=>{
           // 可以利用setState,更新完状态后调用render()
            this.setState({})
        })
    }

10.6.4 优化 简写mapDispatchToProps

// 创建容器组件并且暴露
export default connect(
    state=>({count:state}),
    /* 
        mapDispatchToProps也可以写成一个对象,
        这个对象中的value只要传入对应的action,
        当UI去调用时实际上是调用action的方法,返回一个{type:xx,data:xx}
        那么react-redux就会自动帮我们去派发action
     */
    {
        plus:plusAction
    }
    )(Count)

10.6.5 provider组件的使用

<容器组件 store={store}/>
<容器组件 store={store}/>
<容器组件 store={store}/>

例如上述代码:如果容器组件很多那么需要传递很多store

provider组件的作用:减少容器组件中传递store

使用方法

在index.js中
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {Provider} from "react-redux"
import store from './redux/store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    {/* 在这里使用,表示App下的所有容器组件都有store,也就是和redux和容器组件建立联系 */}
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

10.6.6 实现数据共享

只需要在store.js文件下再去引入combineReducers

// 引入legacy_createStore用来创建store
// 引入applyMiddleware用来支持使用中间件
// 引入combineReducers用来实现数据共享
import {legacy_createStore,applyMiddleware,combineReducers} from "redux"
// 引入支持异步action的中间件
import thunk from "redux-thunk"
// 引入reducer
import count_reducer from "./reducers/count_reducer"
import person_reducer from "./reducers/person_reducer"
// 切记:combineReducers传入的对象 === redux中保存的总状态对象 
export default legacy_createStore(combineReducers({count_reducer,person_reducer}),applyMiddleware(thunk))

在读取状态的时候需要注意:

// 创建容器组件和UI组件建立联系,并且暴露
export default connect(
  	// 切记:combineReducers传入的对象 === redux中保存的总状态对象 
  	// state.xxx  
    (state)=>({calculate:state.count_reducer,personNum:state.person_reducer}),
    {
        plus:plusAction
    }
)(Count)

在这里插入图片描述


总结

以上就是今天要讲的内容,希望对大家有所帮助!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值