5. redux

1. redux简介

文档:https://www.redux.org.cn/

redux是一个专门用于做状态管理的JS库(不是react插件库),可以用在react、angular、vue等项目中,能够集中式管理react应用中多个组件共享的状态。

redux开发者工具:
在Google应用商店搜索 Redux DevTools


使用场景:

  • 某个组件的状态需要让其他组件随时拿到(共享)
  • 一个组件需要改变另一个组件的状态(通信)

在这里插入图片描述

1.1 action

动作的对象。

包含两个属性:

  1. type:标识属性,值为字符串,唯一,必要属性
  2. data:数据属性,值为任意类型,可选属性
{
	type: 'ADD_PERSON',
	data: {
		name: '张三',
		age: 12
	}
}

1.2 reducer

用于初始化状态、加工状态。

加工时会根据旧的state和action来产生新的state的纯函数。

redux的reducer函数必须是一个纯函数。

1.3 store

将state、action和reducer联系在一起的对象。


得到此对象:

  1. import {createStore} from 'index'
  2. import reducer from './reducers
  3. const store = createStore(reducer)

此对象的功能:

  1. getState():得到state
  2. dispatch(action):分发action,触发reducer,产生新的state
  3. subscribe(listener):注册监听,当产生了新的state时,自动调用

2. redux核心API

2.1 legacy_createStore()

用于创建包含指定reducer的store对象

createStore已弃用

2.2 store对象

store对象是redux库最核心的管理对象。


store对象内部维护:

  1. state
  2. reducer

核心方法:

  • getStore()
  • dispatch(action)
  • subscribe(listener)

具体编码:

  • store.getState()
  • store.dispatch({type: ‘方法’, number})
  • store.subscribe(render)

2.3 applyMiddleware()

应用上基于redux的中间件(插件库)

2.4 combineReducers()

合并多个reducer函数


3. 使用redux

想做一个求和案例:

  1. 安装redux:npm install redux

  2. 在src目录下新建redux目录,并创建store.js和count_reducer.js(转为求和服务的reducer文件)文件

在这里插入图片描述

  1. store.js 文件
/**
 * 该文件专门用于暴露一个store对象,整个应用只有一个store对象
 */

// 引入legacy_createStore,用于创建redux中最为核心的store对象
import { legacy_createStore } from 'redux'
// 引入为Count组件服务的reducer
import countReducer from './count_reducer'

// 暴露store
export default legacy_createStore(countReducer)
  1. count_reducer.js 文件

初始化时,preState的值为undefined,action中的type为@@init形式的值,没有data

/**
 * 该文件用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
 * reducrt函数接收两个参数,分别为:之前的状态和动作对象
 */

// 初始化状态
const initState = 0

export default function countReducer(preState = initState, action) {
    // 从action对象中获取 type、data
    const { type, data } = action

    // 根据 type 决定如何加工数据
    switch (type) {
        case 'increment': // 加
            return preState + data
        case 'decrement': // 减
            return preState - data
        default:
            return preState
    }
}
  1. 仅使用react实现的求和组件
import React, { Component } from 'react';
import './index.css'

export default class Count extends Component {
    state = {
        count: 0
    }

    increment = () => {
        const { value } = this.selectNumber
        const { count } = this.state
        this.setState({
            count: count + value * 1
        })
    }

    decrement = () => {
        const { value } = this.selectNumber
        const { count } = this.state
        this.setState({
            count: count - value * 1
        })
    }

    incrementIfOdd = () => {
        const { value } = this.selectNumber
        const { count } = this.state
        if (count % 2 !== 0) {
            this.setState({
                count: count + value * 1
            })
        }
    }

    incrementAsync = () => {
        const { value } = this.selectNumber
        const { count } = this.state
        setTimeout(() => {
            this.setState({
                count: count + value * 1
            })
        }, 500);
    }

    render() {
        return (
            <div>
                <h1>当前求和为:{this.state.count}</h1>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>
                <button onClick={this.increment}>+</button>
                <button onClick={this.decrement}>-</button>
                <button onClick={this.incrementIfOdd}>当前求和为奇数时+</button>
                <button onClick={this.incrementAsync}>异步+</button>
            </div>
        );
    }
}

  1. 改造Count组件
import React, { Component } from 'react';
import './index.css'
import store from '../../redux/store';

export default class Count extends Component {
	// 去除Count组件自身的状态
	// state = {
    //     count: 0
    // }
    
    // 可以直接在入口文件中写,当多个组件使用redux时,只需写一次就能实现状态更新时组件自动更新
    // componentDidMount() {
    //     /**
    //      * redux只负责状态管理,状态更改并不会引起组件更新
    //      * 需要监测redux中状态的变化,只要变化,就调用render
    //      */
    //     store.subscribe(() => {
    //         this.setState({})
    //     })
    // }

    increment = () => {
        const { value } = this.selectNumber
        // 通知 redux 加 value
        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>
                <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>
                <button onClick={this.increment}>+</button>
                <button onClick={this.decrement}>-</button>
                <button onClick={this.incrementIfOdd}>当前求和为奇数时+</button>
                <button onClick={this.incrementAsync}>异步+</button>
            </div>
        );
    }
}

  1. 在入口文件中实现更新组件状态

在这里插入图片描述

  1. 新建count_action.js
export const createIncrementAction = data => ({ type: 'increment', data })
export const createDecrementAction = data => ({ type: 'decrement', data })
  1. 对Count组件进行改造

在这里插入图片描述


4. 异步action

action的值可以是一般对象Object(同步) 或 函数function(异步)。

redux默认是不能进行异步处理的,但某些时候应用中需要在redux中执行异步任务(ajax, 定时器)。


例子:将组件中的异步任务交到action中去实现:

  1. store默认action必须是一个Object类型的一般对象,所以需要通过一个中间件来使用异步action

    可以使用异步中间件:npm install --save redux-thunk

  2. 在store.js中引入异步中间件

在这里插入图片描述

  1. 开启异步任务

在这里插入图片描述
在这里插入图片描述

不过此时这个函数已经是store帮忙调用的,可以不用引入store

异步action中一般都会调用同步action

在这里插入图片描述


5. react-redux

react-redux是一个react插件库,专门用来简化react应用中使用redux。

5.1 react-redux中的组件分类

  1. UI组件
    • 只负责 UI 的呈现,不带有任何业务逻辑
    • 通过props接收数据(一般数据和函数)
    • 不使用任何 Redux 的 API
    • 一般保存在components文件夹下
  2. 容器组件
    • 负责管理数据和业务逻辑,不负责UI的呈现
    • 使用 Redux 的 API
    • 一般保存在containers文件夹下

UI组件都会被容器组件包裹,由容器组件与redux打交道,且容器组件可以随意使用redux的API,而UI组件中则不能使用任何redux的API。


容器组件给UI组件传:(都通过props传递)

  • redux中所保存的状态
  • 用于操作状态的方法

在这里插入图片描述

5.2 相关API

  1. Provider:让所有组件都可以得到state数据

  2. connect:用于包装 UI 组件生成容器组件

  3. mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性

  4. mapDispatchToProps:将分发action的函数转换为UI组件的标签属性


使用:

  1. 改造Count组件,将store相关内容清除,使其称为一个合法的UI组件
import React, { Component } from 'react'
import './index.css'

export default class Count extends Component {
    increment = () => {
        const { value } = this.selectNumber
    }

    decrement = () => {
        const { value } = this.selectNumber
    }

    incrementIfOdd = () => {
        const { value } = this.selectNumber
    }

    incrementAsync = () => {
        const { value } = this.selectNumber
    }

    render() {
        return (
            <div>
                <h1>当前求和为:?</h1>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>
                <button onClick={this.increment}>+</button>
                <button onClick={this.decrement}>-</button>
                <button onClick={this.incrementIfOdd}>当前求和为奇数时+</button>
                <button onClick={this.incrementAsync}>异步+</button>
            </div>
        );
    }
}

  1. 为Count准备容器组件:src -> containers -> Count -> index.jsx
  2. 实现容器组件,需要借助 react-redux:npm i react-redux
/**
 * 容器组件作为UI组件和redux的桥梁,需要将两者引入
 */
// 引入Count的UI组件
import CountUI from '../../components/Count'
import {
    createIncrementAction,
    createDecrementAction,
    createIncrementAsyncAction
} from '../../redux/count_action'

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

/**
 * mapStateToProps函数返回一个对象
 *      返回的对象中的key就作为传递给了UI组件props的key
 *      返回的对象中的value就作为传递给了UI组件props的value
 * 用于传递状态
 */
// function mapStateToProps(state) {
//     return { count: state }
// }
const mapStateToProps = (state) => ({ count: state })

/**
 * mapDispatchToProps函数返回一个对象
 *      返回的对象中的key就作为传递给了UI组件props的key
 *      返回的对象中的value就作为传递给了UI组件props的value
 * 用于传递操作状态的方法
 */
// function mapDispatchToProps(dispatch) {
//     return {
//         add: (data) => {
//             // 通知redux执行加法
//             dispatch(createIncrementAction(data))
//         },
//         des: (data) => {
//             // 通知redux执行减法
//             dispatch(createDecrementAction(data))
//         },
//         addAsync: (data, time) => {
//             // 异步加
//             dispatch(createIncrementAsyncAction(data, time))
//         }
//     }
const mapDispatchToProps = (dispatch) => ({
    add: (data) => {
        // 通知redux执行加法
        dispatch(createIncrementAction(data))
    },
    des: (data) => {
        // 通知redux执行减法
        dispatch(createDecrementAction(data))
    },
    addAsync: (data, time) => {
        // 异步加
        dispatch(createIncrementAsyncAction(data, time))
    }
})
}

// 使用connent创建并暴露Count的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)
  1. 此时在App.js中不能引入Count的UI组件,而要引入Count的容器组件,并引入store

在这里插入图片描述

  1. 在UI容器中使用this.props接收
import React, { Component } from 'react'
import './index.css'

export default class Count extends Component {
    increment = () => {
        const { value } = this.selectNumber
        this.props.add(value * 1)
    }

    decrement = () => {
        const { value } = this.selectNumber
        this.props.des(value * 1)
    }

    incrementIfOdd = () => {
        const { value } = this.selectNumber
        if (this.props.count % 2 !== 0) {
            this.props.add(value * 1)
        }
    }

    incrementAsync = () => {
        const { value } = this.selectNumber
        this.props.addAsync(value * 1, 500)
    }

    render() {
        // console.log(this.props);
        return (
            <div>
                <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>
                <button onClick={this.increment}>+</button>
                <button onClick={this.decrement}>-</button>
                <button onClick={this.incrementIfOdd}>当前求和为奇数时+</button>
                <button onClick={this.incrementAsync}>异步+</button>
            </div>
        );
    }
}

  1. mapDispatchToProps可以简写,因为react-redux做了自动分发
    在这里插入图片描述

  2. index.js文件中不再需要监测redux状态发生改变,容器组件中已经默认拥有监测redux状态发生改变的能力

// 检测redux中状态改变,若redux状态发生改变,则重新渲染App组件
// store.subscribe(() => {
//     ReactDOM.render(<App />, document.getElementById('root'))
// })
  1. 不在App.jsx文件中为容器组件一个个传递store,可以在index.js中将所有容器组件都需要store交给Provider,Provider会自动分析应用里面所有的容器组件,把store传给每一个需要store的容器组件

在这里插入图片描述
在这里插入图片描述

  1. 可以将容器组件和UI组件整合
import React, { Component } from 'react'
import '../../components/Count/index'

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


// 定义UI组件
class Count extends Component {
    increment = () => {
        const { value } = this.selectNumber
        this.props.add(value * 1)
    }

    decrement = () => {
        const { value } = this.selectNumber
        this.props.des(value * 1)
    }

    incrementIfOdd = () => {
        const { value } = this.selectNumber
        if (this.props.count % 2 !== 0) {
            this.props.add(value * 1)
        }
    }

    incrementAsync = () => {
        const { value } = this.selectNumber
        this.props.addAsync(value * 1, 500)
    }

    render() {
        // console.log(this.props);
        return (
            <div>
                <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>
                <button onClick={this.increment}>+</button>
                <button onClick={this.decrement}>-</button>
                <button onClick={this.incrementIfOdd}>当前求和为奇数时+</button>
                <button onClick={this.incrementAsync}>异步+</button>
            </div>
        );
    }
}

// 使用connent创建并暴露Count的容器组件
export default connect((state) => ({ count: state }), {
    add: createIncrementAction,
    des: createDecrementAction,
    addAsync: createIncrementAsyncAction
})(Count)

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值