上节课我们用react
实现了我们的求和案例,那一节没有任何可说的,甚至可以不学那节课,但是通过那节课我们也知道了我们想要的需求究竟是什么样子的了,这节课我们就来引入redux
来实现一下这个demo
。
redux
精简版
我们说了要实现刚才哪个demo
,而刚才哪个demo
很简单,在react
里面一个组件也就完成了,但是接下来我们要来引入redux
,可能很多有学过react
的人说redux
好难的,那么我们先从最精简的模式来实现,我们一步步来,先别给自己太大压力。
结构精简
先来分析我们的redux
工作流程的结构:
组件发起--> Action Creators --> Store --> Reducers --> Store -->
组件获取新state
,那么我们来想一下,Action Creators
是什么?是不是创建动作对象的?那么动作对象是什么?不就是一个普通的js
对象嘛,那么我们自己写这个对象行不行?是不是完全没问题的?一个type
属性,一个data
属性。那我们是不是就完全不需要Action Creators
了?
那么Store
是不是必不可少的?这个是一个核心节点啊,那么Reducers
是不是也得有?不然的话没人干活了啊。
分析
我们来分析一下,我们要有store
,那么我们是不是要建一个store.js
文件?我们要用Reducers,
那么为什么要在结尾加个s
?是不是复数啊?因为每个组件都要有自己的reducer
,那么我们这个是Sum
组件的reducer
,那么是不是就应该创建sum_reducer.js
?我们要能明确这个reducer
是谁的,所以把组件名带上,那么好,我们来把文件创建一下。
紧接着,我们既然要用redux
那么我们是不是要安装这个库啊?脚手架可不知道我们要用这个库,所以说是不会自动帮我们安装的,那么我们来安装一下,执行命令npm i redux
Store
实现
那么我们安装好了redux
,那么我们也该正式来实现了,其实我们在redux
中实现代码的先后顺序到时没有什么要求,但是我们建议初学的时候先来实现最核心的部分,所以我们先来实现store
。
那么store
是个什么呢?我们光说了这个是个调度器,是个存储器,但是我们没有从本质上介绍过这是个什么。既然我们不知道这个是什么那么我们这么去实现store
啊?
而且store
是不是在redux
整个流程的C
位的?那么这么核心的一个东西,那能是我们随便写几行代码就直接搞出来的一个吗?是不是不可能啊?所以说官方在这里提供了一个API
用来创建一个store
。
import { createStore } from 'redux'; import sumReducer from './sum_reducer'; export default createStore(sumReducer);
我们来开一下这里的代码,首先从redux
库中引入一个方法createStore
,那么大家说这个方法是干什么的?这是不是用脚后跟都能想的出来?这个玩意儿就是专门用来创建store
的啊。那么我们调用这个方法的是传了个什么进去?我们从sum_reducer
中导入了一个sumReducer
,这是什么啊?这是不是一个干活的reducer
啊?就好比开一个饭馆,开业的时候老板肯定要在开业前就找好厨师的团队啊。所以说这里也一样,我们创建store
的时候,就要传入reducer
。然后我们把创建的store
暴露出去,这样我们才能够使用。
Reducer
实现
上面我们实现了store
,知道在创建store
的时候是不是要传入一个reducer
?那么我们的reducer
实现了吗?我们的sum_reducer.js
里面是不是什么都还没写呢?那么我们来实现我们的reducer
。
那么我们到现在都不知道reducer
到底是个啥玩意儿,我们怎么去实现呢?首先我们来想一下,我们这个reducer
都能干什么?我们不要去死记一些固定的格式,这个是个函数啊,函数要传什么参数啊,这样没有什么意义,我们先来搞懂他。先来想一想,reducer
能干什么?
reducer
可以初始化state
reducer
可以接收到两个参数- 当前的
state
store
传过来的动作对象reducer
可以根据接收到的参数然后生成新的state
然后返回给store
那么我们来想一下,js
里面什么东西可以接收参数,然后还要返回值?是不是只有函数?所以说reducer
是不是应该就是一个函数啊?
export default function sumReducer(preState, action) { if (preState === undefined) preState = 0; const { type, data } = action; switch (type) { case "increment": return preState + data; case "decrement": return preState - data; default: return preState; } }
我们来看代码,我们知道了这个东西是一个函数,而且这个函数呢要接收两个参数,一个是之前的状态,一个是动作对象,那么我们是不是要从动作对象里面拿到动作类型和数据?然后根据动作类型来判断,我们这里用了switch
的控制流。一个加一个减。
可能有人要问了,那么我们说的奇数时再加还要哪个延迟 1 秒之后再加为啥没有写呢?redux
里面是不关心你这些的,我就给你一个加一个减,这两个操作,你要奇数再加或者延迟 1 秒再加的话你先在组件中或者其他环节判断好,当要进行加操作的时候你再通知reducer
,否则就不通知他干活。
那么我们来看我们的default
。这是在干什么呢?我们之前既没有加也没有减的话应该是什么?是不是初始化?但如果初始化的时候preState
进来的是不是undefined
?那么我们就判断一下,如果是undefined
那么我们就把preState
置 0,然后在把preState
返回出去。这样我们就完成了初始化的操作。
至此我们这个demo
的reducer
也实现了。
组件使用redux
那么我们现在已经实现了store
,也实现了reducer
,那么我们回到组件中,我们来想一下组件中的那些onClick
事件的回调方法应该这么改。
import React, { Component } from 'react' import store from '../../redux/store'; export default class Sum extends Component { render() { return ( <div> <h1>当前求和为:{store.getState()}</h1> <select ref={c => this.selectNum = c}> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.add}>+</button> <button onClick={this.dec}>-</button> <button onClick={this.odd}>odd</button> <button onClick={this.async}>async</button> </div> ) } add = () => {} dec = () => {} odd = () => {} async = () => {} }
我们先别着急看我们的方法,首先我们已经把我们展示的数据都交给redux
管理了,那么我们是不是就不需要在组件内部自己定义state
了?那么我们把state
给删了,我们初始页面展示什么?我们是不是要找redux
拿?那么redux
把我们的state
存在哪了?是不是在store
里面?那么怎么从store
里面拿state
?是不是用getState
方法?那么我们来卡一下效果:
大家看,初始效果已经展示出来了,但是现在这些按钮都还不能点,因为我们的回调都还没有修改。那么我们应该怎么修改这些回调呢?
import React, { Component } from 'react' import store from '../../redux/store'; export default class Sum extends Component { render() {...} add = () => { const { value } = this.selectNum; store.dispatch({ type: "increment", data: value }); } dec = () => {} odd = () => {} async = () => {} }
我们来看代码,我们把其他的代码都隐藏了,就留了加的代码,如果加我们明白了,其他的都是一样的。那么加里面是什么?首先我们是不是还是要根据ref
来获取选择的值?然后我们要通知redux
来干活。那么谁能通知啊?我们按照流程图,是不是要通知Action Creators
然后由store
来居中调度分发动作对象,但是我们是不是这个demo
我们不走Action Creators
?我们自己手写一个动作对象,然后传出去。那么我们说过从组件中发起之后,是不是要有dispatch
函数来分发动作对象,而调度器是谁?是不是store
?那么我们是不是通过store.dispatch
方法就可以完成动作分发了啊?我们来看一下效果:
大家看,我们点击了好多次加号了,但是页面并没有变化,为什么呢?我们来想一想,在react
中只要state
发生变化是不是就会重新调用render
?但是redux
中并没有给你这个机制,redux
又不是react
,redux
只是帮你管理一下react
的状态而已,所以说我们要在组件中监测redux
中的状态,只要发生变化就重新调用render
。那么我们来改一下:
import React, { Component } from 'react' import store from '../../redux/store'; export default class Sum extends Component { componentDidMount(){ store.subscribe(()=>this.setState({})); } render() {...} add = () => {...} dec = () => {...} odd = () => {...} async = () => {...} }
我们来看一下代码,我们是不是要在组件一挂载完成就开始监测redux
的state
?那么就要用componentDidMount
钩子,然后调用store
的subscribe
方法,这个方法要接收一个函数,那么我们这个箭头函数做了什么?setState
但是什么都没修改。我们是不是只要setState
就会自动调render
,那么我们这个箭头函数会在什么时候被调用呢?只要redux
中的state
一发生变化,那么这个箭头函数就会被执行。那么我们这次再来看一下结果:
这次我们便可以正常展示我们的结果了
总结
subscribe API
来监测redux
中的state
getState API
来获取redux
中的state
createStore
来创建store
redux
中不关心其他的东西,制作基础操作redux
中的state
改变不重新调render
Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.
Link of this article:https://work.lynchow.com/article/demo_react_redux_mini/