Redux 文章,助你了解 Web 架构其中的一种解决方案

32 篇文章 0 订阅
3 篇文章 0 订阅

应用场景

多交互、多数据源

用户的使用方式复杂
不同身份的用户有不同的使用方式(比如普通用户和管理员)
多个用户之间可以协作
与服务器大量交互,或者使用了WebSocket
View要从多个来源获取数据

从组件角度看,如果你的应用有以下场景,可以考虑使用 Redux。

某个组件的状态,需要共享
某个状态需要在任何地方都可以拿到
一个组件需要改变全局状态
一个组件需要改变另一个组件的状态

设计思想

Redux 的设计思想很简单,就两句话。

(1)Web 应用是一个状态机,视图与状态是一一对应的。

(2)所有的状态,保存在一个对象里面。

学习文档

  1. 英文文档: https://redux.js.org/

  2. 中文文档: http://www.redux.org.cn/

  3. Github: https://github.com/reactjs/redux

redux 是什么?

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

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

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

redux 工作流程

在这里插入图片描述

对上图进行简单叙述:

首先store是保持数据的容器,且整个应用只能有一个 store

state 对象是包含了所有的数据,可以理解为Store生成的快照,是一个数据集合,用来包装数据的

Action 描述当前要发生的事情,是组件用来改变state唯一方法,
因为组件不能直接去改变state,所以将进行修改的数据它会辅助的运输到store


而dispatch()便是将Action发到Store的工具

当Store收到Action以后,必须给出一个新的State,
这种State的计算过程叫做Renducers

Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State


实际应用中,Reducer 函数不用像上面这样手动调用,
store.dispatch方法会触发 Reducer 的自动执行。
为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,
将 Reducer 传入createStore方法。这样就实现了监听绑定。


==========================================================
 上面的描述可以结合我下面的计数器demo结合看

描述这么多,大体的意思,可以这样理解:

组件想要对state进行操作,需要先将修改的数据传入到Action中,然后再运输到store中
(当Action中的数据刚提交到store中的时候,便会绑定上Reducer函数,这个函数进行处理将旧的store中的state进行对比计算后便会将新的state更新到store tree中,最后组件再去store中获取最新的state)

redux 三个核心

1、action

  1. 标识要执行行为的对象

  2. 包含2个方面的属性

a. type: 标识属性, 值为字符串, 唯一, 必要属性

b. xxx: 数据属性, 值类型任意, 可选属性

  1. 例子:

​ const action = {

​ type: ‘INCREMENT’,

​ data: 2

​ }

  1. Action Creator(创建Action的工厂函数)

​ const increment = (number) => ({type: ‘INCREMENT’, data: number})

2、reducer

  1. 根据老的state和action, 产生新的state的纯函数

  2. 样例

         export default function counter(state = 0, action) {

​          switch (action.type) {

​           case 'INCREMENT':

​            return state + action.data

​           case 'DECREMENT':

​            return state - action.data

​           default:

​            return state

​          }

​         }
  1. 注意

a. 返回一个新的状态

b. 不要修改原来的状态

3、 store

Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。 

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

  2. 如何得到此对象?

import {createStore} from 'redux'

import reducer from './reducers'

const store = createStore(reducer)
  1. 此对象的功能?
getState(): 得到state

dispatch(action): 分发action, 触发reducer调用, 产生新的state

subscribe(listener): 注册监听, 当产生了新的state时, 自动调用

redux的核心API

1、createStore()

1)	作用: 
创建包含指定reducer的store对象
createStore这个函数,用来生成 Store。

2)	编码:

import {createStore} from 'redux'
import counter from './reducers/counter'
const store = createStore(counter)


2、 store对象

1)	作用: 
redux库最核心的管理对象
2)	它内部维护着:
		state
		reducer
3)	核心方法:
		getState()
		dispatch(action)
		subscribe(listener)
4)	编码:
		store.getState()
		store.dispatch({type:'INCREMENT', number})
		store.subscribe(render)


3applyMiddleware()

1)	作用:
应用上基于redux的中间件(插件库)
2)	编码:

import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'  // redux异步中间件
const store = createStore(
  counter,
  applyMiddleware(thunk) // 应用上异步中间件
)


4.、combineReducers()
1)	作用:
合并多个reducer函数
2)	编码:
export default combineReducers({
  user,
  chatUser,
  chat
})

什么情况下需要使用redux

  1. 总体原则: 能不用就不用, 如果不用比较吃力才考虑使用

  2. 某个组件的状态,需要共享

  3. 某个状态需要在任何地方都可以拿到

  4. 一个组件需要改变全局状态

  5. 一个组件需要改变另一个组件的状态

安装

npm install --save redux

计数器(demo)

效果如下图:

在这里插入图片描述

1、在src下面创建index.js入口文件,配置如下:

import React from 'react';
import ReactDOM from 'react-dom';


import App from './components/app.jsx';
import store from './redux/store'




function render() {
  ReactDOM.render(<App  store={store}/>,document.getElementById('root'));
}

// 初始化渲染
render()

// 订阅监听(store状态发生变化,便会自动调用进行重绘)
store.subscribe(render)

2、在src文件夹下面创建components文件夹

(1)此文件夹中包含根组件app.jsx文件,配置如下:

import React, { Component } from 'react'
import * as action from '../redux/actions'


export default class App extends Component {
    increment = () =>{
        
        // 获取增加的数量
        const num = this.select.value*1

        // 调用state更新方法
        this.props.store.dispatch(action.increment(num))
    }

    decrement = ()=> {
        // 获取减少的数量
        const num = this.select.value*1
        
        // 调用state更新方法
        this.props.store.dispatch(action.decrement(num))
    }

    incrementIfOdd = () => {
        // 得到选择的数量
        const num = this.select.value * 1

        // 得到原本的count状态
        const count = this.props.store.getState()

        // 判断,满足条件才更新状态
        if (count % 2 ===1) {
            // 更新状态
            this.props.store.dispatch(action.increment(num))
        }
    }


    render() {
        const count = this.props.store.getState()
        return (
            <div id="app">
                <p>click {count} times</p>
                <div>
                    <select ref={select => this.select = select}>
                        <option value="1">1</option>
                        <option value="2">2</option>
                        <option value="3">3</option>
                    </select>&nbsp;&nbsp;
                    <button onClick={this.increment}>+</button>&nbsp;&nbsp;
                    <button onClick={this.decrement}>-</button>&nbsp;&nbsp;
                    <button onClick={this.incrementIfOdd}>increment if odd</button>
                </div>
            </div>

        )
    }
}

3、在src文件夹下面创建redux文件夹

(1)创建文件 store.js(此文件是创建如redux工作流程图 Action Creators,初始化)

import {createStore} from 'redux'
import {counter} from './reducers'

const store = createStore(counter)
console.log(store)

export default store

(2)创建文件 action.js(此文件用来操作store)

/*
    包含所有的 action creator
*/

import { INCREMENT, DECREMENT } from "./action_type";

// 增加
export const increment = (number) => ({type: INCREMENT, data:number})


// 减少
export const decrement = (number) => ({type: DECREMENT, data:number})

(3)创建文件 action_type.js(定义action配置类型)

/*
     包含所有action type 的常量字符串
*/

export const INCREMENT = 'INCREMENT'
export const DECREMENT = 'DECREMENT'

(4) 创建文件 reducers.js(用来监听store状态,将新的state更新到store)

/**
 * 
 * 包含N个reducer 函数的模板
 * 
 */
import {INCREMENT, DECREMENT}  from './action_type.js'

export function counter(state=0,action) {
    console.log('counter()',state,action)
    switch (action.type) {
        case INCREMENT:
            return state + action.data
        
        case DECREMENT:
            return state - action.data
        
        default:
            return state
    }
}

React-Redux

(1)	一个react插件库
(2)	专门用来简化react应用中使用redux

React-Redux将所有组件分成两大类

1、 UI组件

只负责 UI 的呈现,不带有任何业务逻辑

通过props接收数据(一般数据和函数)

不使用任何 Redux 的 API

一般保存在components文件夹下

2、 容器组件

负责管理数据和业务逻辑,不负责UI的呈现

使用 Redux 的 API

一般保存在containers文件夹下

相关API

1、Provider

让所有组件都可以得到state数据

<Provider store={store}>
   <App />
 </Provider>

2、connect()

用于包装 UI 组件生成容器组件

import { connect } from 'react-redux'
  connect(
   mapStateToprops,
   mapDispatchToProps
  )(Counter)

3、mapStateToprops()

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

  const mapStateToprops = function (state) {
  return {
   value: state
  	}
 }

4、 mapDispatchToProps()

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

简洁语法可以直接指定为actions对象或包含多个action方法的对象

安装

npm install --save react-redux

使用

1、以下文件不变

redux/action-types.js文件不变

redux/actions.js文件不变

redux/reducers.js文件不变

2、将原来components文件夹下面的app.jsx文件改为counter.jsx(作为UI组件)配置如下:

import React, { Component } from 'react'

import PropTypes from 'prop-types'

export default class Counter extends Component {

    static propTypes = {
        count: PropTypes.number.isRequired,
        increment: PropTypes.func.isRequired,
        decrement: PropTypes.func.isRequired
    }

    increment = () =>{
        
        // 获取增加的数量
        const num = this.select.value*1

        // 调用state更新方法
        this.props.increment(num)
    }

    decrement = ()=> {
        // 获取减少的数量
        const num = this.select.value*1
        
        // 调用state更新方法
        this.props.decrement(num)
    }

    incrementIfOdd = () => {
        // 得到选择的数量
        const num = this.select.value * 1

        // 得到原本的count状态
        const {count} = this.props

        // 判断,满足条件才更新状态
        if (count % 2 ===1) {
            // 更新状态
            this.props.increment(num)
        }
    }


    render() {
        const {count} = this.props
        return (
            <div id="app">
                <p>click {count} times</p>
                <div>
                    <select ref={select => this.select = select}>
                        <option value="1">1</option>
                        <option value="2">2</option>
                        <option value="3">3</option>
                    </select>&nbsp;&nbsp;
                    <button onClick={this.increment}>+</button>&nbsp;&nbsp;
                    <button onClick={this.decrement}>-</button>&nbsp;&nbsp;
                    <button onClick={this.incrementIfOdd}>increment if odd</button>
                </div>
            </div>

        )
    }
}

3、修改index.js 文件,配置如下:

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux'

import App from './containers/app'
import store from './redux/store'


ReactDOM.render((
  <Provider  store={store}>
    <App/>
  </Provider>
)
,document.getElementById('root'));

4、创建containers文件夹(此文件夹下面的文件作为容器组件),并在下面创建app.jsx文件,配置如下:

import React, { Component } from 'react'
import {increment,decrement} from '../redux/actions'

import Counter from '../components/counter'

// 用来连接react组件和redux
import {connect} from 'react-redux'


// 返回的是一个新的组件
export default connect(
    state =>({count:state}),
    {increment,decrement}
)(Counter)

问题

  1. redux默认是不能进行异步处理的,

  2. 应用中又需要在redux中执行异步任务(ajax, 定时器)

Redux 异步编程

下载redux插件(异步中间件)

npm install --save redux-thunk

1、index.js

import {createStore, applyMiddleware}  from 'redux' 
import  thunk  from 'redux-thunk'

// 根据 counter 函数创建store对象  
const store = createStore(counter,applyMiddleware(thunk) // 应用上异步中间件 )

2、redux/actions.js

// 异步action creator(返回一个函数) 

export const incrementAsync = number => {return dispatch => {setTimeout(() => {    dispatch(increment(number))}, 1000)  } }

3、 components/counter.jsx

incrementAsync = () => { const number = this.refs.numSelect.value*1 this.props.incrementAsync(number) }

4、containers/app.jsx

import {increment, decrement, incrementAsync} from '../redux/actions'

// 向外暴露连接App组件的包装组件
export default connect(  state => ({count: state}),{increment, decrement, incrementAsync})(Counter)  

Redux调试工具

1. 安装chrome浏览器插件

redux-devtools

2. 下载工具依赖包

npm install --save-dev redux-devtools-extension

3. 编码

import { composeWithDevTools } from 'redux-devtools-extension'  

const store = createStore( counter, composeWithDevTools(applyMiddleware(thunk)))
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值