React——Redux基础

1.Redux初识

1.为什么需要Redux

单单使用react只能做一些简单的项目?

因为React就是一个简单的轻量级的视图层框架。

React当中的组件通信和状态管理是特别繁琐的,比如子组件和父组件通信改变值,要通过父组件的方法。

需要视图层框架+数据层框架,两个相互结合,就可以实现大型的开发项目了。

2.redux官方图片

Redux简化图

2.使用Redux

1.创建store、reducer

安装Redux

1
npm install --save redux

装好redux之后,在src目录下创建一个store文件夹,然后在文件夹下创建一个index.js文件。

index.js就是整个项目的store文件,打开文件,编写下面的代码。

1
2
3
import { createStore } from 'redux'  // 引入createStore方法
const store = createStore() // 创建数据存储仓库
export default store //暴露出去

这样虽然已经建立好了仓库,但是这个仓库很混乱,这时候就需要一个有管理能力的模块出现,这就是Reducers。这两个一定要一起创建出来,这样仓库才不会出现互怼现象。在store文件夹下,新建一个文件reducer.js,然后写入下面的代码。

1
2
3
4
5
6
7
8
9
10
const defaultState = {
inputValue : 'Write Something',
list:[
'早上4点起床,锻炼身体',
'中午下班游泳一小时'
]
}
export default (state = defaultState,action)=>{
return state
}

这样reducer就建立好了,把reducer引入到store中,再创建store时,以参数的形式传递给store。

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

2.组件获得store中的数据

引入store

1
2
import store from './store/index'
import store from './store' //缩写

调用state

1
2
3
4
5
6
7
constructor(props){
super(props)
//关键代码-----------start
this.state=store.getState();
//关键代码-----------end
console.log(this.state)
}

getState是redux实例里面的方法

3.修改state

1.组件里

1
2
3
4
5
6
7
changeInputValue(e){
const action ={
type:'changeInput',
value:e.target.value
}
store.dispatch(action)
}

拼装action参数,并调用store,dispatch方法让store派遣到对应的reducer

2.reducer里

1
2
3
4
5
6
7
8
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
}

reducer里的两个参数:

  • state: 指的是原始仓库里的状态(里只能接收state,不能改变state)。
  • action: 指的是action新传递的状态。

通过打印你可以知道,Reducer已经拿到了原来的数据和新传递过来的数据,现在要作的就是改变store里的值。我们先判断type是不是正确的,如果正确,我们需要从新声明一个变量newState。(记住:Reducer里只能接收state,不能改变state。),所以我们声明了一个新变量,然后再次用return返回回去。

3.让组件发生更新

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

1
2
3
4
5
6
7
8
9
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
}
1
2
3
storeChange(){
this.setState(store.getState())
}

4.总结

redux是通过判断消息类型进行对应操作的模式对state进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
export default (state = defaultState,action)=>{
if(action.type === 'changeInput'){
let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
newState.inputValue = action.value
return newState
}
//关键代码------------------start----------
//state值只能传递,不能使用
if(action.type === 'addItem' ){ //根据type值,编写业务逻辑
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue) //push新的内容到列表中去
newState.inputValue = ''
return newState
}

if(action.type === 'deleteItem' ){
let newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index,1) //删除数组中对应的值
return newState
}


//关键代码------------------end----------
return state
}

3.Redux技巧

1.制作映射文件

Redux Action的时候,我们写了很多Action的派发,产生了很多Action Types,如果需要Action的地方我们就自己命名一个Type,会出现两个基本问题:

  • 这些Types如果不统一管理,不利于大型项目的服用,设置会长生冗余代码。
  • 因为Action里的Type,一定要和Reducer里的type一一对应在,所以这部分代码或字母写错后,浏览器里并没有明确的报错,这给调试带来了极大的困难。

那我司中会把Action Type单独拆分出一个文件。在src/store文件夹下面,新建立一个actionTypes.js文件,然后把Type集中放到文件中进行管理。

1
2
3
export const  CHANGE_INPUT = 'changeInput'
export const ADD_ITEM = 'addItem'
export const DELETE_ITEM = 'deleteItem'

2.把所有的Redux Action放到一个文件里进行管理。

2.编写actionCreators.js文件

编写actionCreators.js文件

有了文件,就可以把actionCreatores.js引入到TodoLisit中。

1
import {changeInputAction} from './store/actionCreatores'

引入后,可以把changeInputValue()方法,修改为下面的样子。

1
2
3
4
changeInputValue(e){
const action = changeInputAction(e.target.value)
store.dispatch(action)
}

/src/store文件夹下面,建立一个心的文件actionCreators.js,先在文件中引入上节课编写actionTypes.js文件。

1
import {CHANGE_INPUT}  from './actionTypes'

引入后可以用const声明一个changeInputAction变量,变量是一个箭头函数,代码如下:

1
2
3
4
5
6
import {CHANGE_INPUT}  from './actionTypes'

export const changeInputAction = (value)=>({
type:CHANGE_INPUT,
value
})

修改todoList中的代码

有了文件,就可以把actionCreatores.js引入到TodoLisit中。

1
import {changeInputAction} from './store/actionCreatores'

引入后,可以把changeInputValue()方法,修改为下面的样子。

1
2
3
4
changeInputValue(e){
const action = changeInputAction(e.target.value)
store.dispatch(action)
}

4.Redux的三个坑

  • store必须是唯一的,多个store是坚决不允许,只能有一个store空间
  • 只有store能改变自己的内容,Reducer不能改变
  • Reducer必须是纯函数

1.Store必须是唯一的

现在看TodoList.js的代码,就可以看到,这里有一个/store/index.js文件,只在这个文件中用createStore()方法,声明了一个store,之后整个应用都在使用这个store。 下面我给出了index.js内容,可以帮助你更好的回顾。

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

2.只有store能改变自己的内容,Reducer不能改变

很多新手小伙伴会认为把业务逻辑写在了Reducer中,那改变state值的一定是Reducer,其实不然,在Reducer中我们只是作了一个返回,返回到了store中,并没有作任何改变。我这个在上边的课程中也着重进行了说明。我们再来复习一下Reducer的代码,来加深印象。

Reudcer只是返回了更改的数据,但是并没有更改store中的数据,store拿到了Reducer的数据,自己对自己进行了更新。

3.Reducer必须是纯函数

先来看什么是纯函数,纯函数定义:

如果函数的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化,必须只依赖于其输入参数。

其实你可以简单的理解为返回的结果是由传入的值决定的,而不是其它的东西决定的。比如下面这段Reducer代码。