技术胖Redux免费教程 2万字长文章 (二)(技术胖gitee)
P13:进阶-组件UI和业务逻辑的拆分方法
【前端迷】是一个公益性的前端技术分享社区,不定期为前端开发者带来面试经历,源码解析以及技术分享,欢迎大家订阅。
欢迎关注,回复【教程】获取最新教学资源视频,回复【pdf】获取前端开发热门书籍pdf。
微信扫码关注
Redux的基础知识都学完了,但是你离高手还差一点东西,就是如何拆分UI部分和业务逻辑,让项目更容易维护。你可能会问了除了更容易维护,还有没有其它好处,肯定是有的。能拆分了,就代表能更多人协作,实现超大型项目的开发和快速上线。比如两个人同时写一个模块,一个写UI部分,一个写业务逻辑部分,之后两个人在一起整合。也许小公司你觉的这样的优势不明显,因为公司的财力或者开发人员不足,使得这种开发方法大大受到了限制。但是大公司,不缺钱,不缺人,抢的就是时间,这时候这种开发模式就可以解决问题。这也是我为什么强烈推荐你去大公司的原因,虽然技术都一样,但是大公司和小公司开发的模式是完全不一样的。有点跑题了,言归正传,看看到底如何把一个组件的UI和业务逻辑拆分出来。
拆分UI组件
可以看到TodoList.js组件是UI和业务逻辑完全耦合在一起的,这时候在src目录下新建一个文件叫TodoListUI.js,快速生成页面的基本结构.
import React, { Component } from \'react\'; class TodoListUi extends Component { render() { return ( <div>123</div> ); } } export default TodoListUi; 复制代码
然后去TodoList.js里把JSX的部分拷贝过来, 现在的代码如下(当然现在是不可以使用的,好多东西我们还没有引入,所以会报错):
import React, { Component } from \'react\'; class TodoListUi extends Component { render() { return ( <div style={{margin:\'10px\'}}> <div> <Input placeholder={this.state.inputValue} style={{ width:\'250px\', marginRight:\'10px\'}} onChange={this.changeInputValue} value={this.state.inputValue} /> <Button type=\"primary\" onClick={this.clickBtn} >增加</Button> </div> <div style={{margin:\'10px\',width:\'300px\'}}> <List bordered dataSource={this.state.list} renderItem={(item,index)=>(<List.Item onClick={this.deleteItem.bind(this,index)}>{item}</List.Item>)} /> </div> </div> ); } } export default TodoListUi; 复制代码
要想可用,第一步是需要引入antd的相关类库,这时候你可以拷贝TodoList.js的相关代码,把antd的CSS和用到的组件都拷贝过来,进行引入。
import \'antd/dist/antd.css\' import { Input , Button , List } from \'antd\' 复制代码
但是这并没有TodoListUI.js组件所需要的state(状态信息),接下来需要改造父组件进行传递值。
TodoList.js文件的修改
TodoList.js里就不需要这么多JSX代码了,只要引入TodoListUI.js。
import TodoListUI from \'./TodoListUI\' 复制代码
引入之后render函数就可以写成下面这个样子。
render() { return ( <TodoListUI /> ); } 复制代码
这样就算做完UI和业务分离的第一步了,剩下的就是改变TodoListUI.js里边的属性了,也就是两个组件的整合。
UI组件和业务逻辑组件的整合
其实整合就是通过属性传值的形式,把需要的值传递给子组件,子组件接收这些值,进行相应的绑定就可以了。这个步骤比较多,还是看视频学习吧。
TodoList.js的render部分
render() { return ( <TodoListUI inputValue={this.state.inputValue} list={this.state.list} changeInputValue={this.changeInputValue} clickBtn={this.clickBtn} deleteItem={this.deleteItem} /> ); } 复制代码
你还需要在constructor(构造函数里)对deleteItem方法进行重新绑定this,代码如下。
this.deleteItem = this.deleteItem.bind(this) 复制代码
修改完TodoList.js文件,还要对UI组件进行对应的属性替换,所有代码如下。
import React, { Component } from \'react\'; import \'antd/dist/antd.css\' import { Input , Button , List } from \'antd\' class TodoListUi extends Component { render() { return ( <div style={{margin:\'10px\'}}> <div> <Input style={{ width:\'250px\', marginRight:\'10px\'}} onChange={this.props.changeInputValue} value={this.props.inputValue} /> <Button type=\"primary\" onClick={this.props.clickBtn} >增加</Button> </div> <div style={{margin:\'10px\',width:\'300px\'}}> <List bordered dataSource={this.props.list} renderItem={(item,index)=>(<List.Item onClick={(index)=>{this.props.deleteItem(index)}}>{item}</List.Item>)} /> </div> </div> ); } } export default TodoListUi; 复制代码
需要注意的是在List组件的删除功能,需要用箭头函数的形式,代替以前方法,并在箭头函数里使用属性的方法,调用出啊你过来的方法。
<List bordered dataSource={this.props.list} renderItem={(item,index)=>(<List.Item onClick={(index)=>{this.props.deleteItem(index)}}>{item}</List.Item>)} /> 复制代码
这些都做完了,你就已经把组件进行了拆分,其实这节课学习的目的不是拆分的步骤,而是拆分的思想,你可以反复几次来加深对UI和业务逻辑拆分的理解。前端免费课程就找技术胖,下节课再见。
P14:进阶-填坑和Redux中的无状态组件
上节课程序写完,有一个小错误,当时我并没注意到,还是VIP群里的小伙伴告诉我的,无意中给大家留了一个坑,跟大家说对不起了。这节课我们先解决这个遗留问题,再讲一下无状态组件。无状态组件其实就是一个函数,它不用再继承任何的类(class),当然如名字所一样,也不存在state(状态)。因为无状态组件其实就是一个函数(方法),所以它的性能也比普通的React组件要好。
胖哥翻车填坑
上节课写完UI和业务分离后,在删除TodoList的项目时,是有一个错误的,这个错误属于业务逻辑错误,并不是语法错误。就是在删除item时,正序删除是没有问题的,但是倒叙删除是有问题的。 主要是我们的index出现了重新声明的问题。
原来的错误代码是这样的:
<List bordered dataSource={this.props.list} renderItem={(item,index)=>(<List.Item onClick={(index)=>{this.props.deleteItem(index)}}>{item}</List.Item>)} /> 复制代码
只要改成下面这样就正确了。
<List bordered dataSource={this.props.list} renderItem={ (item,index)=>( <List.Item onClick={()=>{this.props.deleteItem(index)}}> {item} </List.Item> ) } /> 复制代码
无状态组件的改写
把UI组件改成无状态组件可以提高程序性能,具体来看一下如何编写。
- 首先我们不在需要引入React中的{ Component },删除就好。
- 然后些一个TodoListUI函数,里边只返回JSX的部分就好,这步可以复制。
- 函数传递一个props参数,之后修改里边的所有props,去掉this。
这里给出最后修改好以后的无状态组件代码,这样的效率要高于以前写的普通react组件。
import React from \'react\'; import \'antd/dist/antd.css\' import { Input , Button , List } from \'antd\' const TodoListUi = (props)=>{ return( <div style={{margin:\'10px\'}}> <div> <Input style={{ width:\'250px\', marginRight:\'10px\'}} onChange={props.changeInputValue} value={props.inputValue} /> <Button type=\"primary\" onClick={props.clickBtn} >增加</Button> </div> <div style={{margin:\'10px\',width:\'300px\'}}> <List bordered dataSource={props.list} renderItem={ (item,index)=>( <List.Item onClick={()=>{props.deleteItem(index)}}> {item} </List.Item> ) } /> </div> </div> ) } export default TodoListUi; 复制代码
总结:这节课主要学习了React中的无状态组件,如果是以前没有Redux的时候,实现分离是比较困难的,但是现在我们作项目,一定想着找个组件是否可以作成无状态组件。如果能做成无状态组件就尽量作成无状态组件,毕竟性能要高很多。
P15:进阶-Axios异步获取数据并和Redux结合
这节课是最近几天小伙伴问我比较多的问题,就是从后端接口获取了数据,如何可以放到Redux的store中,很多小伙伴被这个困难卡住了。这节课就来学习一下如何从后台取得数据,并和Redux结合,实现想要的业务逻辑。比如以前我们的列表数据是在Reducer里写死的,这节课使用Axios从后台获取数据。
利用easy-mock创建模拟数据
这个在基础课程中已经讲过了,我就不作过多的介绍了,如果你还不会,就直接看基础课程吧,反复讲也没什么意思。如果你说我也懒得新建一个,你也可以使用我的模拟数据,我在这里给出地址。
地址:www.easy-mock.com/mock/5cfcce…
JSON的基本格式,如果上面的接口不管用了,你可以用Easy mock自己作一个这样的接口:
{ \"data\": { \"list\": [ \'早上4点起床,锻炼身体\', \'中午下班游泳一小时\', \'晚上8点到10点,学习两个小时\' ] } } 复制代码
安装并使用Axios
因为在Redux的学习中,我们使用了新的项目和目录,所以要重新安装Axios插件(以前安装的不能再使用了)。直接使用npm进行安装。
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) }) } 复制代码
做完这一步骤后,可以在浏览器中打开,预览下是否控制台(console)获取数据,如果可以获取,说明完全正常。
获取数据后跟Redux相结合(重点)
接下来就可以把得到的数据放到Redux的store中了,这部分和以前的知识都一样,我就尽量给出代码,少讲理论了。 先创建一个函数,打开以前写的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 } 复制代码
这样就完成了一次从后台请求数据,然后和Redux结合的过程。希望小伙伴都能练习一下,我们的程序员越来越像真实的开发了,小伙伴也要在练习中不断熟悉这种开发模式。
P16:进阶-Redux-thunk中间件的安装和配置
通过学习,你已经对Redux的基本流程有了全面的了解,也许你已经在项目中开始使用。其实咱们一起学完了大部分的Redux知识,但是我还是决定继续讲解一下Redux-thunk这个Redux最常用的插件。什么时候会用到这个插件那?比如在Dispatch一个Action之后,到达reducer之前,进行一些额外的操作,就需要用到middleware(中间件)。在实际工作中你可以使用中间件来进行日志记录、创建崩溃报告,调用异步接口或者路由。 这个中间件可以使用是Redux-thunk来进行增强(当然你也可以使用其它的),它就是对Redux中dispatch的加强,这节课我们先来学习一下安装和配置(特别是配置的使用很多小伙伴都配置不成功)。
安装Redux-thunk组件
Redux-thunk并不在Redux基础组件中,也就是说需要进行新安装。安装使用npm就可以了。
npm install --save redux-thunk 复制代码
在终端命令行输入上面的命令,就可以进行安装了,根据网络不同安装的时间也会有些不同,我办公室的网是秒按,家里的宽带需要10分钟左右。
配置Redux-thunk组件
安装作起来很容易,但是配置就要稍微注意一下了,这里边还是有几个小坑的,如果你完全按照官方文档是配置不成功的。 需要在创建store的地方引入redux-thunk,对于我们的目录来说,就是/store/index.js文件。
1.引入applyMiddleware,如果你要使用中间件,就必须在redux中引入applyMiddleware.
import { createStore , applyMiddleware } from \'redux\' 复制代码
2.引入redux-thunk库
import thunk from \'redux-thunk\' 复制代码
如果你按照官方文档来写,你直接把thunk放到createStore里的第二个参数就可以了,但以前我们配置了Redux Dev Tools,已经占用了第二个参数。
官方文档给的方法:
const store = createStore( reducer, applyMiddleware(thunk) ) // 创建数据存储仓库 复制代码
这样写是完全没有问题的,但是我们的Redux Dev Tools插件就不能使用了,如果想两个同时使用,需要使用增强函数。使用增加函数前需要先引入compose。
import { createStore , applyMiddleware ,compose } from \'redux\' 复制代码
然后利用compose创造一个增强函数,就相当于建立了一个链式函数,代码如下:
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose 复制代码
有了增强函数后,就可以把thunk加入进来了,这样两个函数就都会执行了。
const enhancer = composeEnhancers(applyMiddleware(thunk)) 复制代码
这时候直接在createStore函数中的第二个参数,使用这个enhancer变量就可以了,相当于两个函数都执行了。
const store = createStore( reducer, enhancer) // 创建数据存储仓库 复制代码
也许你对增加函数还不能完全理解,其实你完全把这个想成固定代码,直接使用就好,我在这里给出全部代码,方便你以后学习使用。
import { createStore , applyMiddleware ,compose } from \'redux\' // 引入createStore方法 import reducer from \'./reducer\' import thunk from \'redux-thunk\' const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose const enhancer = composeEnhancers(applyMiddleware(thunk)) const store = createStore( reducer, enhancer) // 创建数据存储仓库 export default store //暴露出去 复制代码
这样就算把Redux的中间件配置好了,可以运行项目,到浏览器看一下结果和看一下Redux Dev Tools插件了。
P17:进阶-Redux-thunk的使用方法
这节课我们把向后台请求数据的程序放到中间件中,这样就形成了一套完整的Redux流程,所有逻辑都是在Redux的内部完成的,这样看起来更完美,而且这样作自动化测试也会变动简单很多,所以工作中你还是要尽量按照这种写法来写。现在就开始学习吧。
在actionCreators.js里编写业务逻辑
以前actionCreators.js都是定义好的action,根本没办法写业务逻辑,有了Redux-thunk之后,可以把TodoList.js中的componentDidMount业务逻辑放到这里来编写。也就是把向后台请求数据的代码放到actionCreators.js文件里。那我们需要引入axios,并写一个新的函数方法。(以前的action是对象,现在的action可以是函数了,这就是redux-thunk带来的好处)
import axios from \'axios\' ...something... export const getTodoList = () =>{ return ()=>{ axios.get(\'https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList\').then((res)=>{ const data = res.data console.log(data) }) } } 复制代码
现在我们需要执行这个方法,并在控制台查看结果,这时候可以修改TodoList.js文件中的componentDidMount代码。
//先引入getTodoList import {getTodoList , changeInputAction , addItemAction ,deleteItemAction,getListAction} from \'./store/actionCreatores\' ---something--- componentDidMount(){ const action = getTodoList() store.dispatch(action) } 复制代码
然后我们到浏览器的控制台中查看一下,看看是不是已经得到了后端传给我们的数据,如果一切正常,应该是可以得到。得到之后,我们继续走以前的Redux流程就可以了。
export const getTodoList = () =>{ return (dispatch)=>{ axios.get(\'https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList\').then((res)=>{ const data = res.data const action = getListAction(data) dispatch(action) }) } } 复制代码
这个函数可以直接传递dispatch进去,这是自动的,然后我们直接用dispatch(action)传递就好了。现在我们就可以打开浏览器进行测试了。
这时候还会有一些警告,主要是我们引入了并没有使用,我们按照警告的提示,删除没用的引入就可以了。
也许你会觉的这么写程序很绕,其实我刚开始写Redux的时候也会这么想,但是随着项目的越来越大,你会发现把共享state的业务逻辑放到你Redux提示当中是非常正确的,它会使你的程序更加有条理。而在自动化测试的时候,可以直接对一个方法进行测试,而对生命周期测试是困难的。我目前接触的大公司都是要求这样写的,如果现在还不能理解里边的好处,也不用纠结,先按照这种形式进行编写。等你写过2至3个项目后,你就能理解这种写法的好处了。
P18:进阶-Redux-saga的安装和配置
先来说明一点,我们这里讲的中间件不是React的中间件,而是Redux的中间件,这一点你要明白,否则工作中会出大问题的,你的React知识架构也会出现偏差。其实Redux的中间件不仅仅只有Redux-thunk,还有一个比较出名的是Redux-saga.当然这个中间件我们公司并没有使用,只是自己研究,所以可能讲解有不足的地方。目前国内的IT企业一般都在使用这两个中间件,使用其它的很少,这个就像可口可乐和百事可乐,所以很有必要学习一下。
redux-saga的安装
你可以直接使用npm来进行安装,当然用yarn也是没有问题的,根据自己的喜好吧。我这里使用了npm来进行安装。
npm install --save redux-saga 复制代码
这里给出github地址,方便你更好的学习。
github.com/redux-saga/…
引入并创建Redux-saga
安装好后,就可以使用了,直接在/src/store/index.js里引入redux-saga,并创建一个sagaMiddleware,代码如下:
import createSagaMiddleware from \'redux-saga\' //引入saga const sagaMiddleware = createSagaMiddleware(); //创建saga中间件 复制代码
创建好后,还是用Redux的增强函数进行传递。也就是把原来的Redux-thunk替换成saga中间件(注意去掉原来不用的redux-thunk引入)。
import { createStore , applyMiddleware ,compose } from \'redux\' // 引入createStore方法 import reducer from \'./reducer\' //------关键代码----start----------- import createSagaMiddleware from \'redux-saga\' const sagaMiddleware = createSagaMiddleware(); //------关键代码----end----------- const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose //------关键代码----start----------- const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware)) //------关键代码----end----------- const store = createStore( reducer, enhancer) // 创建数据存储仓库 export default store //暴露出去 复制代码
这步完成后,就把原来的redux-thunk替换成redux-saga了,当然,现在我们还不能使用,我们还需要继续配置sagas.js文件。
创建sagas.js文件并引入
redux-saga希望你把业务逻辑单独写一个文件,这里我们在/src/store/文件夹下建立一个sagas.js文件。
function* mySaga() {} export default mySaga; 复制代码
创建好以后直接引入到index.js里。
import mySagas from \'./sagas\' 复制代码
然后执行run方法,让saga运行起来。
sagaMiddleware.run(mySagas) 复制代码
为了方便你学习,这里给出/src/store/index.js的全部内容。
import { createStore , applyMiddleware ,compose } from \'redux\' // 引入createStore方法 import reducer from \'./reducer\' import createSagaMiddleware from \'redux-saga\' import mySagas from \'./sagas\' const sagaMiddleware = createSagaMiddleware(); const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware)) const store = createStore( reducer, enhancer) sagaMiddleware.run(mySagas) export default store 复制代码
现在已经完全替换成redux-saga了,所以以前在TodoList.js中的代码需要删除,不删除就会报错。主要删除componentDidMount声明周期函数里的代码。 这样redux-saga的安装和配置就算完成了,之后我们就可以编写中间件了。其实这个配置一般在项目中也只需要作一次,你完全可以收藏网页,然后需要时回来看一下就可以了。
P19:进阶-用Redux-saga获取TodoList列表
上节课已经完成了redux-saga的安装和基本配置,这篇文章就用Redux-saga来完成TodoList的列表获取。其实redxu-saga是比redux-thunk要复杂的,它多出了很多API需要学习,至少是学习成本增加了。但是有的人说saga更适合于大型项目,本人不予表态,也不想引战,如果你的公司用了saga,这两篇文章足可以让你入门了。话不多说,我们继续学习。
编写TodoList.js文件
我们先来改造TodoList.js文件,现在componentDidMount里边是空的,所以我们要进行redux的基本操作,这个流程我不再多作介绍了,已经练习了10几遍了。
当然可以先引入一个action,当然这个action还没有,我们一会再进行编写,给它起名叫做getMyListAction(你可以起任何名字,记住就好,因为下面我们要不断使用)
import {getMyListAction, changeInputAction , addItemAction ,deleteItemAction} from \'./store/actionCreatores\' 复制代码
然后顺势在actionCreators.js文件里把这个action创建出来。
export const getMyListAction = ()=>({ type:GET_MY_LIST }) 复制代码
写完你会发现GET_MY_LIST也没有,需要先引入,再到actionTypes.js里进行定义
import {GET_MY_LIST,CHANGE_INPUT , ADD_ITEM,DELETE_ITEM,GET_LIST} from \'./actionTypes\' 复制代码
actionTypes.js文件定义GET_MY_LIST
export const GET_MY_LIST = \'getMyList\' 复制代码
之后就可以回到TodoList.js文件,编写componentDidMount里的内容了。
componentDidMount(){ const action =getMyListAction() store.dispatch(action) console.log(action) } 复制代码
测试完成,可以删除console.log(),保持代码的简洁和没有冗余代码。
编写sagas.js文件(也是业务逻辑)
用saga的中间件业务逻辑,就都写在这个sagas.js文件里,文件里我们用mySaga来作为入口函数。在入口函数中捕获传递过来的action类型,根据类型不同调用不同的方法。
import { takeEvery } from \'redux-saga/effects\' import {GET_MY_LIST} from \'./actionTypes\' //generator函数 function* mySaga() { //等待捕获action yield takeEvery(GET_MY_LIST, getList) } function* getList(){ console.log(\'jspang\') } export default mySaga; 复制代码
写完上面的代码,我们看一下是否可以正确在浏览器的控制台打印出结果,如果可以顺利的打印出来,说明到目前为止制作正确。然后接下来我们就要用axios来请求结果了。
这里给出sagas.js的所有内容,然后详细的意思在视频中进行讲解。
import { takeEvery ,put } from \'redux-saga/effects\' import {GET_MY_LIST} from \'./actionTypes\' import {getListAction} from \'./actionCreatores\' import axios from \'axios\' //generator函数 function* mySaga() { //等待捕获action yield takeEvery(GET_MY_LIST, getList) } function* getList(){ //这段代码我就不删除了。 // axios.get(\'https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList\').then((res)=>{ // const data = res.data // const action = getListAction(data) // put(action) // }) const res = yield axios.get(\'https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList\') const action = getListAction(res.data) yield put(action) } export default mySaga; 复制代码
总结:这就是Redux-saga的基本使用方法,其实saga还有其它一些API,但是我工作中用的也不是很多,所以这里也只能保证你达到入门的水平,至于深入,你可以自己探索。至于redux-thunk和redux-saga哪个好的问题,这里不作争论,用网上流行的话说,小孩子才做选择题,技术老鸟全都学。
P20:进阶-React-Redux介绍和安装
React-Redux这是一个React生态中常用组件,它可以简化Redux流程,这节课我们就重新建立一个项目Demo02,然后会利用几节课的时间用React-redux把之前的TodoList案例重新实现一遍。如果你公司不用这个插件,其实没必要耗费时间学。但是作为一篇文章,必须保证知识尽可能完整。(需要注意的是概念:React、Redux、React-redux是三个不同的东西)
React项目初始化
因为以前已经安装了脚手架工具creat-react-app,所以现在直接在项目的终端中输入下面的命令。
create-react-app demo02 cd demo02 npm start 复制代码
经过上面的三个命令,应该可以在浏览器中出现下面的界面(出现画面说明我们项目初始化完成)。
安装完成后,删除一些没有必要的样式和代码,在/src目录下,只留一个index.js文件,其余的全部删除,这时候项目已经不能启动起来了,这很正常。
import React from \'react\'; import ReactDOM from \'react-dom\'; ReactDOM.render(<App />, document.getElementById(\'root\')); 复制代码
安装react-redux
项目初始化好后,直接使用npm在命令行安装React-redux,这个网络的不同安装时间也有所不同。
npm install --save react-redux 复制代码
视频录制视时安装的版本是7.1.0版本,你学习的时候可能跟我有所不同,如有不同,可以到Github上查询最新API文档。
修改代码,让他跑起来
目前项目还是没办法跑起来的,需要建立一个TodoList.js的组件。项目代码如下:
import React, { Component } from \'react\'; class TodoList extends Component { render() { return ( <div>JSPang</div> ); } } export default TodoList; 复制代码
有了TodoList.js后,我们引入到index.js文件下,然后修改代码如下:
import React from \'react\'; import ReactDOM from \'react-dom\'; import TodoList from \'./TodoList\' ReactDOM.render(<TodoList />, document.getElementById(\'root\')); 复制代码
这时候再在浏览器中预览,就会只输出一个JSPang的字样。虽然很丑,但是项目已经跑起来了。接下来我们编写一下render函数中的JSX页面(为了节省大家的时间,就不再使用antd了)。
render() { return ( <div> <div><input /><button>提交</button></div> <ul> <li>JSPang</li> </ul> </div> ); } 复制代码
这时候界面应该发生了一点变化,这样基本的项目我们就算初始化完成了,接下来我们按原来的Redux方式作一个store出来。
Redux的安装和使用(复习)
先在终端中安装Redux包,因为是一个新项目,所以需要重新安装。
npm install --save redux 复制代码
首先创建一个store文件夹,在/store下创建一个index.js文件,并写入下面代码:
import {createStore} from \'redux\' import reducer from \'./reducer\' const store = createStore(reducer) export default store 复制代码
目前我们还没有reducer,所以我们要创建reducer.js文件,代码如下:
const defalutState = { inputValue : \'jspang\', list :[] } export default (state = defalutState,action) =>{ return state } 复制代码
然后再TodoList.js中的构造函数constructor中使用。
import React, { Component } from \'react\'; //-----关键代码--------start import store from \'./store\' //-----关键代码--------end class TodoList extends Component { //-----关键代码--------start constructor(props){ super(props) this.state = store.getState() } //-----关键代码--------end render() { return ( <div> <div> //-----关键代码--------start <input value={this.state.inputValue} /> //-----关键代码--------end <button>提交</button> </div> <ul> <li>JSPang</li> </ul> </div> ); } } export default TodoList; 复制代码
写完这段,到浏览器中保存看一下,应该就得到store中的值了,到目前为止,我们只是安装了React-Redux,但是还并没有进行使用,这节课只要是把基本的环境搭建好和复习一下以前的知识。下节课我们再逐步学习React-Redux的知识,小伙伴们先不要着急,先把开发环境搭建好吧。
P21:进阶-React-redux中的Provider和connect
上节课已经完成了React-redux开发TodoList组件的基本环境。现在就可以开心的学习React-redux了,这节课主要学习一下Provider和connect这两个知识点。
<Provider>提供器讲解
<Provider>是一个提供器,只要使用了这个组件,组件里边的其它所有组件都可以使用store了,这也是React-redux的核心组件了。有了<Provider>就可以把/src/index.js改写成下面的代码样式,具体解释在视频中介绍了。
import React from \'react\'; import ReactDOM from \'react-dom\'; import TodoList from \'./TodoList\' //---------关键代码--------start import { Provider } from \'react-redux\' import store from \'./store\' //声明一个App组件,然后这个组件用Provider进行包裹。 const App = ( <Provider store={store}> <TodoList /> </Provider> ) //---------关键代码--------end ReactDOM.render(App, document.getElementById(\'root\')); 复制代码
写完这个,我们再去浏览器中进行查看,发现代码也是可以完美运行的。需要注意的是,现在还是用传统方法获取的store中的数据。有了Provider再获取数据就没有那么麻烦了。
connect连接器的使用
现在如何简单的获取store中数据那?先打开TodoList.js文件,引入connect,它是一个连接器(其实它就是一个方法),有了这个连接器就可以很容易的获得数据了。
import {connect} from \'react-redux\' //引入连接器 复制代码
这时候暴露出去的就变成了connect了,代码如下。
export default connect(xxx,null)(TodoList); 复制代码
这里的xxx代表一个映射关系,目前还没有制作这个映射关系。
映射关系的制作
映射关系就是把原来的state映射成组件中的props属性,比如我们想映射inputValue就可以写成如下代码。
const stateToProps = (state)=>{ return { inputValue : state.inputValue } } 复制代码
这时候再把xxx改为stateToProps
export default connect(stateToProps,null)(TodoList) 复制代码
然后把<input>里的state标签,改为props,代码如下:
<input value={this.props.inputValue} /> 复制代码
为了方便你学习,我这里给出所有的TodoList.js的所有代码。
import React, { Component } from \'react\'; import store from \'./store\' import {connect} from \'react-redux\' class TodoList extends Component { constructor(props){ super(props) this.state = store.getState() } render() { return ( <div> <div> <input value={this.props.inputValue} /> <button>提交</button> </div> <ul> <li>JSPang</li> </ul> </div> ); } } const stateToProps = (state)=>{ return { inputValue : state.inputValue } } export default connect(stateToProps,null)(TodoList); 复制代码
写完之后再到浏览器中查看一下,发现我们映射的关系也是可以用的。这节课就是React-Redux插件的使用重点,你需要多写几遍,把这个流程记在心里。先到这里,下节课我们继续实现TodoList组件。
P22:进阶-React-redux的数据修改
上节课已经可以用React-redux顺利的拿到Store中数据了。这节课学习如何改变Store中的数据。也就是当我们修改<input>中的值时,去改变store数据,UI界面也随之进行改变。
编写onChange响应事件
打开TodoList.js文件,然后在<button>上注册onChange事件,这里我就偷懒直接绑定this了。
<input value={this.props.inputValue} onChange={this.inputChange.bind(this)} /> 复制代码
有了事件需要编写对应的方法,这里先写一个最简单的inputChange方法。
inputChange(e){ console.log(e.target.value) } 复制代码
然后到浏览器中的控制台就不再有报错,而且输入时可以打印出值,这书名我们的绑定成功了。这步完成我们要改为react-redux的了。
编写DispatchToProps
要使用react-redux,我们可以编写另一个映射DispatchToProps,先看下面这段代码,你会发现有两个参数,第二个参数我们用的是null。
export default connect(stateToProps,null)(TodoList); 复制代码
DispatchToProps就是要传递的第二个参数,通过这个参数才能改变store中的值。
const dispatchToProps = (dispatch) =>{ return { inputChange(e){ console.log(e.target.value) } } } 复制代码
有了这个参数之后可以把响应事件改成下面的代码.
<input value={this.props.inputValue} onChange={this.props.inputChange} /> 复制代码
然后把connect第二个参数传递过去。
export default connect(stateToProps,dispatchToProps)(TodoList); 复制代码
这时候原来的inputChange方法就没用了,可以删除掉。 目前整体的代码就改为下面的样子了,我们在浏览器中预览也是可以看到效果的。此步骤成功说明映射关系支持成功。
import React, { Component } from \'react\'; import store from \'./store\' import {connect} from \'react-redux\' class TodoList extends Component { constructor(props){ super(props) this.state = store.getState() } render() { return ( <div> <div> <input value={this.props.inputValue} onChange={this.props.inputChange} /> <button>提交</button> </div> <ul> <li>JSPang</li> </ul> </div> ); } } const stateToProps = (state)=>{ return { inputValue : state.inputValue } } const dispatchToProps = (dispatch) =>{ return { inputChange(e){ console.log(e.target.value) } } } export default connect(stateToProps,dispatchToProps)(TodoList); 复制代码
派发action到store中
映射关系已经做好了,接下来只要进行action的派发和reducer对业务逻辑的编写就可以了。派发action和以前的流程一样,我就直接给出代码了。
const dispatchToProps = (dispatch) =>{ return { inputChange(e){ let action = { type:\'change_input\', value:e.target.value } dispatch(action) } } } 复制代码
派发后就需求在reducer里边,编写对应的业务逻辑了。
const defalutState = { inputValue : \'jspang\', list :[] } export default (state = defalutState,action) =>{ if(action.type === \'change_input\'){ let newState = JSON.parse(JSON.stringify(state)) newState.inputValue = action.value return newState } return state } 复制代码
这样就算整个修改过程完成了,到浏览器中查看一下,应该就实现了改变input框的效果。这个流程你刚开始学会觉的很绕,但是你作的多了,你就会发现它很简单,就是一个模式,而且会降低程序出错的机率。建议这个流程你至少要写5遍以上,据我所知,几乎所有公司用react都会用到react-redux,所以这个流程重要性不次于Redux的流程,一定要熟练掌握。
P23:进阶-React-redux增加List数据
这节课主要学习一下如何用React-Redux增加列表数据,如果你上节课的流程练习熟练了,这节课就不是很难了。这节课要实现的效果,就是点击提交按钮时,可以在列表中进行增加。
给<button>按钮增加点击事件
直接在/src/TodoList.js里的Button增加一个onClick事件,代码如下:
<button onClick={this.props.clickButton}>提交</button> 复制代码
注意这里依然使用的props,也就是说还需要把方法写在dispatchToProps里。我们这里先写一个测试,看看是否绑定上了。
const dispatchToProps = (dispatch) =>{ return { inputChange(e){ let action = { type:\'change_input\', value:e.target.value } dispatch(action) }, clickButton(){ console.log(\'111111111\') } } } 复制代码
写完clickButton方法后,在浏览器中预览,打开浏览器的控制台看一下结果,应该在点击时,可以看到显示111111111。 这步完成,就是用dispatch派发action了。
clickButton(){ let action = { type:\'add_item\' } dispatch(action) } 复制代码
编写Reducer的业务逻辑
派发完成后,到Reducer编写业务逻辑,这一步和一起的操作基本一样。
const defalutState = { inputValue : \'jspang\', list :[] } export default (state = defalutState,action) =>{ if(action.type === \'change_input\'){ let newState = JSON.parse(JSON.stringify(state)) newState.inputValue = action.value return newState } //----关键代码------start--------- if(action.type === \'add_item\'){ let newState = JSON.parse(JSON.stringify(state)) newState.list.push(newState.inputValue) newState.inputValue = \'\' return newState } //----关键代码------end--------- return state } 复制代码
页面UI部分的制作
这步完成后,我们到TodoList.js中进行JSX部分的编写,编写前需要先把stateToProps的映射关系做好。
const stateToProps = (state)=>{ return { inputValue : state.inputValue, list:state.list } } 复制代码
有了映射关系,就可以再界面中用属性的方式,进行显示,代码如下:
<ul> { this.props.list.map((item,index)=>{ return (<li key={index}>{item}</li>) }) } </ul> 复制代码
这样就实现了增加TodoList的列表项,这里给出TodoList.js的代码,方便学习使用.
import React, { Component } from \'react\'; import store from \'./store\' import {connect} from \'react-redux\' class TodoList extends Component { constructor(props){ super(props) this.state = store.getState() } render() { return ( <div> <div> <input value={this.props.inputValue} onChange={this.props.inputChange} /> <button onClick={this.props.clickButton}>提交</button> </div> <ul> { this.props.list.map((item,index)=>{ return (<li key={index}>{item}</li>) }) } </ul> </div> ); } } const stateToProps = (state)=>{ return { inputValue : state.inputValue, list:state.list } } const dispatchToProps = (dispatch) =>{ return { inputChange(e){ let action = { type:\'change_input\', value:e.target.value } dispatch(action) }, clickButton(){ let action = { type:\'add_item\' } dispatch(action) } } } export default connect(stateToProps,dispatchToProps)(TodoList); 复制代码
还有一个删除功能我就不浪费大家时间继续制作了,如果你自己有兴趣可以试着作一下。下节课我们主要讲一下目前代码的优化,这样让你在工作中看起来更专业些。
P24:加餐-React-redux程序优化(完结)
这节课把现在写的代码优化一下,作程序的都应该有一些代码洁癖,才能写出让人称赞的程序。写完业务逻辑后作代码优化,也是程序员的本质工作之一。
用结构复制精简代码
现在代码中有好几处this.props都是重复的,这时候就可以用javascript的解构赋值方法,来精简代码。修改TodoList.js中的Render函数,把原来带代码修改为下面的代码:
render() { let {inputValue ,inputChange,clickButton,list} = this.props; return ( <div> <div> <input value={inputValue} onChange={inputChange} /> <button onClick={clickButton}>提交</button> </div> <ul> { list.map((item,index)=>{ return (<li key={index}>{item}</li>) }) } </ul> </div> ); } 复制代码
把TodoList改为UI组件-提高性能
可以看到,现在的TodoList组件里没有任何的业务逻辑,只有一个Render方法,这时候就可以把它改为UI组件(无状态组件),UI组件就是一个方法,减少很多冗余操作,从而提高程序运行性能。这时候重新声明一个TodoList的变量,然后把render函数里的东西复制过来,只要稍加修改,就可以得到下面的代码:
const TodoList =(props)=>{ let {inputValue ,inputChange,clickButton,list} = props; // 粘贴过来后,此处要进行修改 return ( <div> <div> <input value={inputValue} onChange={inputChange} /> <button onClick={clickButton}>提交</button> </div> <ul> { list.map((item,index)=>{ return (<li key={index}>{item}</li>) }) } </ul> </div> ); } 复制代码
代码写完后,我们删除一些不用的引入,然后就可以到浏览器中进行预览了。
import React from \'react\'; import {connect} from \'react-redux\' 复制代码
为了更好的学习,我在这里给出目前TodoList.js的所有代码。
import React from \'react\'; import {connect} from \'react-redux\' const TodoList =(props)=>{ let {inputValue ,inputChange,clickButton,list} = props; // 粘贴过来后,此处要进行修改 return ( <div> <div> <input value={inputValue} onChange={inputChange} /> <button onClick={clickButton}>提交</button> </div> <ul> { list.map((item,index)=>{ return (<li key={index}>{item}</li>) }) } </ul> </div> ); } const stateToProps = (state)=>{ return { inputValue : state.inputValue, list:state.list } } const dispatchToProps = (dispatch) =>{ return { inputChange(e){ let action = { type:\'change_input\', value:e.target.value } dispatch(action) }, clickButton(){ let action = { type:\'add_item\' } dispatch(action) } } } export default connect(stateToProps,dispatchToProps)(TodoList); 复制代码
那我们反过来,再来理解一下最后一句话代码的意思。
export default connect(stateToProps,dispatchToProps)(TodoList); 复制代码
connect的作用是把UI组件(无状态组件)和业务逻辑代码的分开,然后通过connect再链接到一起,让代码更加清晰和易于维护。这也是React-Redux最大的有点。
Redux的教程和视频到这里就结束了,下套课程我会讲解React-router,请小伙伴们持续关注博客,获得的最新的学习视频。
★《布宫号》提醒您:民俗信仰仅供参考,请勿过度迷信!