redux体验

在网上看过不少资料文档后,确认redux基础概念并不是想象中的难,为啥这么说呢,因为在我还没接触react时就到处听人说redux很难,所以我一直以为它很难上手。但在实际接触后,发现基本的概念并不难,甚至比当初刚接触vuex时更简单。当然了,难的我也还没接触到,现在只说最基础的。

核心概念

关于redux的概念其实我也讲不好的,为了好理解,我尽量用自己的话描述下,到底对不对,说不好~

  • store:可以看成是一个容器,保存数据的地方,要注意的是,整个应用只能有一个 Store

  • state:包含具体的数据,通过store.getState()可以拿到当前的数据,并且一个 State 对应一个 View

  • action:state的变化会导致view的更新,页面中我们是不能直接接触state的,应该是触发view才能导致state变化,可以理解为action就是view发出的通知,表示state要变化了

  • reducer:在发出action通知后,要更新state了,这个更新state的过程就是reducer,它是一个函数,接收action和state作为参数,对应更新state

我知道,刚来就看这么多概念是没用的,可以结合下面说的例子慢慢看。

感受一下redux

通常人们都把redux和react在一起说,但事实上这两个是解耦的,redux不一定要用在react应用上。为了演示方便,我们还是使用create-react-app脚手架,不过开始时,我们不结合react,而是单独看看它的使用。

将脚手架src目录下的所有文件全删除,新建一个index.js文件,写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 引入createStore,用以创建store
import {createStore} from 'redux'

// 创建reducer
function reducer(state=0, action){
switch(action.type){
case 'ADD':
return state+1
case 'REMOVE':
return state-1
default:
return 10
}
}

// 新建store
const store = createStore(reducer)

//查看当前state
console.log(store.getState())

当你敲完这些代码,运行后,可以在控制台看到输出了10。我们来一行行看。

首先,引入了createStore,只看名字我们也知道了,是用来创建store的,它是一个函数,传入reducer作为参数即可创建出store。

再来看reducer函数,它并不是一定要以reducer作为函数名的,你可以自定义。它有两个参数,一个是state,它就是包含具体数据的,可以初始化为0,它可以是字符串,对象等多种类型。第二个参数action是view层传过来的,它包含通知信息,保存在type属性中,它的type属性值也是我们自己定义的,根据传来的信息进行不同的state更新。上面代码还没涉及到action的分发,后面再说。

接下来,就是把新建的reducer作为参数传入到createStore中,从而创建store了,store中的getState方法可以拿到当前state。输出的结果是10,因为我们并没有发送action来提示更新state,所以默认返回了10,这些看代码也很好理解。那么接下来,我们尝试发送一次更新需求:

1
2
3
4
5
// 发送ADD需求
store.dispatch({type:'ADD'})

// 再查看当前state
console.log(store.getState())

结合reducer的代码,也可以明白现在打印的就是11了,action就是通过dispatch发送的,它本质上就是一个对象,一般type属性必写,之后也可以自定义其他属性。好了,现在redux的基本概念已经走了一遍,当然实际中不可能这么简单,这里只做演示。接下来,我们结合react再来写一个实际的小例子。

简单计数器

结合react来做一个简单的计数器吧,把index.js文件改成如下:

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
26
27
28
29
30
31
32
import React from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'redux'
import reducer from './reducer.js'

const store = createStore(reducer)

class App extends React.Component{
remove(){
store.dispatch({
type:'REMOVE'
})
}

add(){
store.dispatch({
type:'ADD',
text:2
})
}
render(){
return (
<div>
<input type="button" value="-" onClick={this.remove.bind(this)}/>
<span>{store.getState()}</span>
<input type="button" value="+" onClick={this.add.bind(this)}/>
</div>
)
}
}

ReactDOM.render(<App/>,document.getElementById('root'))

上面代码也没有啥可说的,都看得懂,注意的是我在add函数中的action里多加了一个text属性,这个属性也是能被传到reducer里的,可以用于state的计算。

再来新建一个reducer.js文件,用来写reducer文件:

1
2
3
4
5
6
7
8
9
10
export default function reducer(state=0,action){
switch(action.type){
case 'ADD':
return state + action.text
case 'REMOVE':
return state - 1
default:
return state
}
}

现在运行浏览器,可以看到页面效果了,但点击按钮时,你会发现数字并没有增加,打开控制台,也没有报错。这是什么原因呢?

原因是在于没有监听state的变化,view是不会自动更新state的,我们需要手动监听state的变化,这时我们需要store.subscribe这个方法,Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。那么想一想,我们监听谁呢?

其实监听的方式不止一种,在这里有一种比较好理解的。每当state变化时,监听函数就会自动执行,那我们让组件重新渲染不就行了,重新渲染的组件一定是最新的state,所以我们可以将index.js文件的最后改造一下:

1
2
3
4
5
6
7
function render(){
ReactDOM.render(<App/>,document.getElementById('root'))
}

render()

store.subscribe(render)

这时,我们就可以在页面中执行数字的加减了。注意到,当点击加时,会加2个数,是因为执行加的时候,是加上了action.text,参考代码。

好了,redux的基本使用就这些了,一些更高级的用法以后再说。(其实看我懒不懒了~)