5. redux

Posted 友人A ㅤ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 以上是关于5. redux的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS 设备上使用 Redux-Persist 和 React Native 时崩溃

DateOffset Pandas 减法

Excel 2007减法得数变成了5E-05怎么办?

Python 5行代码 搞定加减法计算

5. redux

5. redux