redux

redux

React 只是 DOM 的一个抽象层,并不是 Web 应用的完整解决方案。有两个方面,它没涉及。

  • 代码结构
  • 组件之间的通信

2014年 Facebook 提出了 Flux 架构的概念,引发了很多的实现。2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。

如果你不知道是否需要 Redux,那就是不需要它

只有遇到 React 实在解决不了的问题,你才需要 Redux

简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。

  • 用户的使用方式非常简单
  • 用户之间没有协作
  • 不需要与服务器大量交互,也没有使用 WebSocket
  • 视图层(View)只从单一来源获取数据

需要使用redux的项目:

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

从组件层面考虑,什么样子的需要redux:

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

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

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

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

redux的设计思想

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

  2. 所有的状态,保存在一个对象里面(唯一数据源)。

redux的流程

1.store通过reducer创建了初始状态
2.view通过store.getState()获取到了store中保存的state挂载在了自己的状态上
3.用户产生了操作,调用了actions 的方法
4.actions的方法被调用,创建了带有标示性信息的action
5.actions内部通过调用store.dispatch方法将标志性的action发送到了reducer中
6.reducer接收到action并根据标识信息判断之后返回了新的state
7.store的state被reducer更改为新state的时候,store.subscribe方法里的回调函数会执行,此时就可以通知view去重新获取state

注意:flux、redux都不是必须和react搭配使用的,因为flux和redux是完整的架构,在学习react的时候,只是将react的组件作为redux中的视图层去使用了。

reducer是纯函数

reducer是state最终格式的确定。它是一个纯函数,也就是说,只要传入参数相同,返回计算得到的下一个 state 就一定相同。

没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
reducer对传入的action进行判断,然后返回一个通过判断后的state,这就是reducer的全部职责

Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。

纯函数是函数式编程的概念,必须遵守以下一些约束。

不得改写参数

不能调用异步的API

不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果

由于 Reducer 是纯函数,就可以保证同样的State,必定得到同样的 View。但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象,请参考下面的写法。

// State 是一个对象
function reducer(state, action) {
  return Object.assign({}, state, { thingToChange });
  // 或者
  return { ...state, ...newState };
}

// State 是一个数组
function reducer(state, action) {
  return [...state, newItem];
}

最好把 State 对象设成只读。你没法改变它,要得到新的 State,唯一办法就是生成一个新对象。这样的好处是,任何时候,与某个 View 对应的 State 总是一个不变的对象。

redux有四个组成部分

store:用来存储数据
reducer:真正的来管理数据
actionCreators:创建action,交由reducer处理
view: 用来使用数据,在这里,一般用react组件来充当

1.创建store

​ 从redux工具中取出createStore去生成一个store

2.创建一个reducer,然后将其传入到createStore中辅助store的创建

​ reducer是一个纯函数,接收当前状态和action,返回一个状态,返回什么,store的状态就是什么,需要注意的是,不能直接操作当前状态,而是需要返回一个新的状态

想要给store创建默认状态其实就是给reducer一个参数创建默认值

3.组件通过调用store.getState方法来使用store中的数据

4.组件产生用户操作,调用actionCreator的方法创建一个action,利用store.dispatch方法传递给reducer

5.reducer对action上的标示性信息做出判断后对新状态进行处理,然后返回新状态,这个时候store的数据就会发生改变 reducer返回什么状态,store.getState就可以获取什么状态

6.我们可以在组件中,利用store.subscribe(callback)方法去订阅数据的变化,也就是可以传入一个函数,当数据变化的时候,传入的函数会执行,在这个函数中让组件去获取最新的状态

划分Reducer

在一个复杂的尤其是单页面的应用中,为了提高开发效率,我们都会采取协同开发的模式,因为系统的模块较多,各个大的功能板块都较为独立,而redux有一个思想:单一数据源,也就是store只能有一个。

所以,我们多会使用划分reducer的方法,将每个大板块所需维护的状态划分在不同的reducer中去管理,分别有自己的一套结构,再通过combineReducers合并在一起

需要注意的是,划分reducer之后,store会将数据也去根据划分之后的reducer来进行分开管理

// store/reducer.js文件
import {combineReducers} from "redux"
// 引入了分支的reducer文件
import todolist from "./todolist/reducer"

const reducer = combineReducers({
    todolist   //后续组件想要获取redux最新状态的话,需要store.getState().todolist.todos
})
export default reducer;



//TodoContent.js组件里面  获取数据的话需要按照拆分的reducer名称进行数据的模块获取
class TodoContent extends Component {
    constructor(){
        super()
        this.state = {
            todos:store.getState().todolist.todos 
        }
    }
    componentWillMount(){
        //监听 状态的更改  订阅状态变化
        //一旦状态改变了,这个函数就会执行
        store.subscribe(()=>{
            this.setState({
                todos: store.getState().todolist.todos
            })
        })
    }
	.....此处省略代码
	todos.map报错了? todos获取不到
}

react-redux

cnpm install react-redux -S

核心组件:
Provider 提供者 属性上通过store将数据派给容器组件
connect 用于连接容器组件与UI组件

connect() 返回一个函数,函数参数接收UI组件,返回容器组件

connect(mapStateToProps,mapDispatchToProps)(ui组件)

​ 容器组件内部帮你做了 store.subscribe() 的方法订阅
​ 状态变化 ==> 容器组件监听状态改变了 ==> 通过属性的方式传给UI组件

​ 把store.getState()的状态转化为展示组件的props使用

转化为展示组件props上的属性

const mapStateToProps = state=>{
	return state.todolist
}

​ 转化为展示组件props上的方法 (TodoInput.js)

 const mapDispatchToProps = dispatch=>{
     return {
         addNewTodo:title=>{
             let action = actionCreators.addNewTodo(title)
             dispatch(action)
         }
     }
 }
 
 
另外一种写法:
const mapDispatchToProps = actionCreators  //actionCreators = {方法1,方法2}

redux中间件

redux-thunk的一些介绍

通常情况下,action只是一个对象,不能包含异步操作,这导致了很多创建action的逻辑只能写在组件中,代码量较多也不便于复用,同时对该部分代码测试的时候也比较困难,组件的业务逻辑也不清晰,使用中间件了之后,可以通过actionCreator异步编写action,这样代码就会拆分到actionCreator中,可维护性大大提高,可以方便于测试、复用,同时actionCreator还集成了异步操作中不同的action派发机制,减少编码过程中的代码量

做异步的操作在action里面去实现!需要安装redux中间件
redux-thunk redux-saga redux-promise

redux-thunk原理:

可以看出来redux-thunk最重要的思想,就是可以接受一个返回函数的action creator。如果这个action creator 返回的是一个函数,就执行它,如果不是,就按照原来的next(action)执行。
正因为这个action creator可以返回一个函数,那么就可以在这个函数中执行一些异步的操作。换言之,redux的中间件都是对store.dispatch()的增强

dispatch一个action之后,到达reducer之前,进行一些额外的操作,就需要用到middleware。你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。

高阶组件

高阶函数:一个函数内部返回一个新的函数,内部采用闭包的写法。

var add = x => {
  return y => {
     return x+y
 }
}

高阶组件:(HOC) Higher-Order-Component

高阶组件本质上就是一个函数,内部可以接收一个组件,然后返回新的组件。

例如: React.memo() connect()

封装一个具有版权信息的高阶组件withCopy,内部接收一个组件,最终返回一个新的组件。

import React, { Component } from 'react'
//高阶组件,本质上是一个函数  withCopy(Comp) 返回一个新的组件
//高阶组件内部可以对传入进来的组件传递一些新的属性给他,那样的话Comp组件就可以通过this.props获取到外部传入来的属性了
//connect()(UI组件)  ===> 大家就理解了UI组件的props上面就可以获取到redux状态与更改redux状态的方法了

const withCopy = Comp =>{
    return class WithCopyRight extends Component{
        render(){
            return (
                <div>
                    <Comp {...this.props}/>
                    &copy;千锋教育
                </div>
            )
        }
    }
}

export default withCopy

About组件:

import React, { Component } from 'react'
//引入高阶组件withCopy
import withCopy from "./withCopy"
class About extends Component {
    render() {
        return (
            <div>
                About {this.props.info}
            </div>
        )
    }
}
export default withCopy(About)

App组件:

<div>
	<About info={"这是app给他传过去的"}/>
</div>

对于CRA的定制

​ 因为我们发现,调用高阶组件的时候采用withCopy(About)代码,但是不够简洁优雅。

​ 那么我们可以采用装饰器的写法实现调用高阶组件

@withCopy
class About extends Component {
    render() {
        return (
            <div>
                About {this.props.info}
            </div>
        )
    }
}
export default About

很不幸,目前的cra是不支持这种写法,我们需要单独进行定制,让其支持这种写法。

  1. yarn add react-app-rewired

  2. 对脚手架进行轻微的调整

   "scripts": {
   -   "start": "react-scripts start",
   +   "start": "react-app-rewired start",
   -   "build": "react-scripts build",
   +   "build": "react-app-rewired build",
   -   "test": "react-scripts test --env=jsdom",
   +   "test": "react-app-rewired test --env=jsdom",
       "eject": "react-scripts eject"
   }
  1. yarn start启动项目发现报错了,原因是因为根路径下面缺少 config-overrides.js文件
module.exports = function override(config, env) {
    return config;
}

但是启动的时候,发现还是报错,原因是缺少对于decorators-legacy的支持。

  1. 如果想配置的更加方便的话,需要安装 customize-cra , config-overrides文件

    yarn add customize-cra (前提需要react-app-rewired安装)

const {
    addDecoratorsLegacy,
    override
} = require("customize-cra");

module.exports = override(
    addDecoratorsLegacy()   //就代表让当前的cra支持decorators了!
)

其实就会发现 react面向社区化的。它把一些常用到的包都放在社区,用到的话单独去查找去进行相应的配置。

vue的话面向官方化,很多东西内部都帮助我们去实现了。 vue.config.js文件 标准

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

别来…无恙

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

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

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

打赏作者

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

抵扣说明:

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

余额充值