Redux从基础到高级

Redux介绍

Redux是一个用来管理管理数据状态和UI状态的JavaScript应用工具。随着JavaScript单页应用(SPA)开发日趋复杂,JavaScript需要管理比任何时候都要多的state(状态),Redux就是降低管理难度的。(Redux支持React,Angular、jQuery甚至纯JavaScript)
如果不用Redux,我们要传递state是非常麻烦的。Redux中,可以把数据先放在数据仓库(store-公用状态存储空间)中,这里可以统一管理状态,然后哪个组件用到了,就去stroe中查找状态。如果途中的紫色组件想改变状态时,只需要改变store中的状态,然后其他组件就会跟着中的自动进行改变。

Fulx和Redux的区别

Redux就是Flux的升级版本,早期使用React都要配合Flux进行状态管理,但是在使用中,Flux显露了很多弊端,比如多状态管理的复杂和易错。所以Redux就诞生了,现在已经完全取代了Flux。

Redux工作流程

你只有学会了Redux工作流程,你才能对Redux有个通透的了解。
在这里插入图片描述
初学者一般是看不懂上面官方文档说明的工作流程的,下面我举一个例子来让大家更容易理解。

ReactComponents 相当于就是 一个人去买烟,然后进入超市见到超市管理人员 就相当于ActionCreators 我说我想要一盒玉溪,那么超市管理人员(ActionCreators)就会去卖烟区(store)让卖烟负责人(Reducers)去找玉溪 最后给到我(ReactComponents )手里

进入主题

编写页面

为了方便学习,我给出了全部代码,如果你作起来有难度,可以直接复制下面的代码。

import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { Input , Button , List } from 'antd'

const data=[
    '早8点开晨会,分配今天的开发工作',
    '早9点和项目经理作开发需求讨论会',
    '晚5:30对今日代码进行review'
]
class TodoList extends Component {
    render() { 
        return ( 
            <div style={{margin:'10px'}}>
                <div>
                    <Input placeholder='write someting' style={{ width:'250px', marginRight:'10px'}}/>
                    <Button type="primary">增加</Button>
                </div>
                <div style={{margin:'10px',width:'300px'}}>
                    <List
                        bordered
                        dataSource={data}
                        renderItem={item=>(<List.Item>{item}</List.Item>)}
                    />    
                </div>
            </div>
         );
    }
}
export default TodoList;

创建Redux中的仓库-store和reducer

  1. 在使用Redux之前,需要先用npm install来进行安装,打开终端,并进入到项目目录,然后输入。
npm install --save redux
  1. 安装好redux之后,在src目录下创建一个store文件夹,然后在文件夹下创建一个index.js文件。
    index.js就是整个项目的store文件,打开文件,编写下面的代码。
import { createStore } from 'redux'  // 引入createStore方法
const store = createStore()          // 创建数据存储仓库
export default store                 //暴露出去
  1. 这样虽然已经建立好了仓库,但是这个仓库很混乱,这时候就需要一个有管理能力的模块出现,这就是Reducers。这两个一定要一起创建出来,这样仓库才不会出现互怼现象。在store文件夹下,新建一个文件reducer.js,然后写入下面的代码。
const defaultState = {}  //默认数据
export default (state = defaultState,action)=>{  //就是一个方法函数
    return state
}
  • state: 是整个项目中需要管理的数据信息,这里我们没有什么数据,所以用空对象来表示。
    这样reducer就建立好了,把reducer引入到store中,再创建store时,以参数的形式传递给store。
import { createStore } from 'redux'  //  引入createStore方法
import reducer from './reducer'    
const store = createStore(reducer) // 创建数据存储仓库
export default store   //暴露出去
  1. 在store中为todoList初始化数据
    仓库store和reducer都创建好了,可以初始化一下todoList中的数据了,在reducer.js文件的defaultState对象中,加入两个属性:inputValue和list。代码如下
const defaultState = {
    inputValue : 'Write Something',
    list:[
        '早上4点起床,锻炼身体',
        '中午下班游泳一小时'
    ]
}
export default (state = defaultState,action)=>{
    return state
}
  • 这就相当于你给Store里增加了两个新的数据。
  1. 组件获得store中的数据
    有了store仓库,也有了数据,那如何获得stroe中的数据呐?你可以在要使用的组件中,先引入store。 我们todoList组件要使用store,就在src/TodoList.js文件夹中,进行引入。
import store from './store/index'

引入store后可以试着在构造方法里打印到控制台一下,看看是否真正获得了数据,如果一切正常,是完全可以获取数据的。

constructor(props){
    super(props)
    console.log(store.getState())
}

这时候数据还不能在UI层让组件直接使用,我们可以直接复制给组件的state,代码如下(我这里为了方便学习,给出全部代码了).

import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { Input , Button , List } from 'antd'
import store from './store'



class TodoList extends Component {
constructor(props){
    super(props)
    //关键代码-----------start
    this.state=store.getState();
    //关键代码-----------end
    console.log(this.state)
}
    render() { 
        return ( 
            <div style={{margin:'10px'}}>
                <div>

                    <Input placeholder={this.state.inputValue} style={{ width:'250px', marginRight:'10px'}}/>
                    <Button type="primary">增加</Button>
                </div>
                <div style={{margin:'10px',width:'300px'}}>
                    <List
                        bordered
                        //关键代码-----------start
                        dataSource={this.state.list}
                        //关键代码-----------end
                        renderItem={item=>(<List.Item>{item}</List.Item>)}
                    />    
                </div>
            </div>
         );
    }
}
export default TodoList;

通过上面的步骤,我们从仓库里取出了数据,并用在组件的UI界面上,也算是一个小小的进步了。 这节课我们讲了如何创建store,reduce和如何使用store中的数据。

Redux Dev Tools的安装

安装

我使用Chrome浏览器安装插件,在浏览器右上角有三个点,然后点击"更多工具",再点击"扩展程序",再点击右侧的"打开Chrome网上商店",然后搜索Redux DevTools,可以看到下面这个插件,直接安装就可以了。

配置 Redux Dev tools

我们把store文件夹下的index.js代码改为下面的样子。

import { createStore } from 'redux'  //  引入createStore方法
import reducer from './reducer'    
const store = createStore(reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) // 创建数据存储仓库
export default store   //暴露出去

其实就是加了这样一句话.

window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()

这句话的意思就是看window里有没有这个方法,有则执行这个方法(不要被大写的方法名吓到)。
这步完成后,就可以启动项目测试一下了,你会发现State数据变的一目了然,以后再进行Redux调试,就会变的非常简单了。

通过Input体验Redux的流程

通过Input的改变,体验一下Redux的整体流程,是如何编写代码的。我们要实现的是在TodoList的Demo中,只要文本框中的值改变就redux中store的值就跟着改变,并且随着Redux中的state值改变,组件也跟着改变。

增加Input响应事件

如果想Input改变,redux也跟着改变,需要在Input组件上增加onChange响应事件, 打开src目录下的ToDolist.js文件,修改具体代码如下:

<Input 
    placeholder={this.state.inputValue} 
    style={{ width:'250px', marginRight:'10px'}}
    //---------关键代码----start
    onChange={this.changeInputValue}
    //---------关键代码----end
/>

写完这一步,还要记得在constructor进行this的绑定,修改this的指向。

constructor(props){
    super(props)
    this.state=store.getState();
    this.changeInputValue= this.changeInputValue.bind(this)
}

这步完成后,就可以编写changeInputValue方法的代码了。我们先在控制台打印出文本框的变化,代码如下:

changeInputValue(e){
    console.log(e.target.value)
}

创建Action

想改变Redux里边State的值就要创建Action了。Action就是一个对象,这个对象一般有两个属性,第一个是对Action的描述,第二个是要改变的值。

changeInputValue(e){
    const action ={
        type:'change_input_value',
        value:e.target.value
    }
}

action就创建好了,但是要通过dispatch()方法传递给store。我们在action下面再加入一句代码。

changeInputValue(e){
    const action ={
        type:'changeInput',
        value:e.target.value
    }
    store.dispatch(action)
}

store的自动推送策略

store只是一个仓库,它并没有管理能力,它会把接收到的action自动转发给Reducer。打开store文件夹下面的reducer.js文件,修改代码。

  • state: 指的是原始仓库里的状态。
  • action: 指的是action新传递的状态。
export default (state = defaultState,action)=>{
    if(action.type === 'changeInput'){
        let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
        newState.inputValue = action.value
        return newState
    }
    return state
}

让组件发生变化

现在store里的数据已经更新了,但是组件还没有进行更新,我们需要打开组件文件TodoList.js,在constructor,写入下面的代码。

constructor(props){
    super(props)
    this.state=store.getState();
    this.changeInputValue= this.changeInputValue.bind(this)
    //----------关键代码-----------start
    this.storeChange = this.storeChange.bind(this)  //转变this指向
    store.subscribe(this.storeChange) //订阅Redux的状态
    //----------关键代码-----------end
}

当然我们现在还没有这个storeChange方法,只要写一下这个方法,并且重新setState一次就可以实现组件也是变化的。在代码的最下方,编写storeChange方法。

 storeChange(){
     this.setState(store.getState())
 }

Axios异步获取数据并和Redux结合

安装并使用Axios

npm install --save axios

安装完成后,就可以在TodoList.js中,引入并进行使用了。

import axios from 'axios'

引入后,在组件的声明周期函数里componentDidMount获取远程接口数据。

componentDidMount(){
    axios.get('https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList').then((res)=>{
        console.log(res)
    })
}

获取数据后跟Redux结合

先创建一个函数,打开以前写的store/actionCreatores.js函数,然后写一个新的函数,代码如下:

export const getListAction  = (data)=>({
    type:GET_LIST,
    data
})

这时候保存会显示找不到GET_LIST,我们再到actionTypes.js文件中加入一个常量,然后引入到actionCreatores.js中

export const  GET_LIST = 'getList'

引入到actionCreatores.js中

import {CHANGE_INPUT , ADD_ITEM , DELETE_ITEM , GET_LIST}  from './actionTypes'

这步完成后,回到TodoList.js文件,继续编写axios中的回调内容,在写之前,记得先把getListAction进行引入。

import {changeInputAction , addItemAction ,deleteItemAction,getListAction} from './store/actionCreatores'
componentDidMount(){
    axios.get('https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList').then((res)=>{    
        const data = res.data
        const action = getListAction(data)
        store.dispatch(action)
    })
}

在数据已经通过dispatch传递给store了,接下来就需要reducer处理业务逻辑了。打开reducer.js代码如下(详细步骤在代码中作了注释):

//----关键代码--------start --------引入GET_LIST
import {CHANGE_INPUT,ADD_ITEM,DELETE_ITEM,GET_LIST} from './actionTypes'
//----关键代码--------end 
const defaultState = {
    inputValue : 'Write Something',
    //----关键代码--------start --------删除原来的初始化代码,减少冗余
    list:[]
}
export default (state = defaultState,action)=>{
    if(action.type === CHANGE_INPUT){
        let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
        newState.inputValue = action.value
        return newState
    }
    if(action.type === ADD_ITEM ){ 
        let newState = JSON.parse(JSON.stringify(state)) 
        newState.list.push(newState.inputValue)  //push新的内容到列表中去
        newState.inputValue = ''
        return newState
    }
    if(action.type === DELETE_ITEM ){ //根据type值,编写业务逻辑
        let newState = JSON.parse(JSON.stringify(state)) 
        newState.list.splice(action.index,1)  //push新的内容到列表中去
        return newState
    }
    //----关键代码--------start --------
    if(action.type === GET_LIST ){ //根据type值,编写业务逻辑
        let newState = JSON.parse(JSON.stringify(state)) 
        newState.list = action.data.data.list //复制性的List数组进去
        return newState
    }
    //----关键代码--------en'd --------

    return state
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值