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
- 在使用Redux之前,需要先用npm install来进行安装,打开终端,并进入到项目目录,然后输入。
npm install --save redux
- 安装好redux之后,在src目录下创建一个store文件夹,然后在文件夹下创建一个index.js文件。
index.js就是整个项目的store文件,打开文件,编写下面的代码。
import { createStore } from 'redux' // 引入createStore方法
const store = createStore() // 创建数据存储仓库
export default store //暴露出去
- 这样虽然已经建立好了仓库,但是这个仓库很混乱,这时候就需要一个有管理能力的模块出现,这就是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 //暴露出去
- 在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里增加了两个新的数据。
- 组件获得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
}