技术胖Redux免费教程 2万字长文章 (二)(技术胖gitee)

P13:进阶-组件UI和业务逻辑的拆分方法

【前端迷】是一个公益性的前端技术分享社区,不定期为前端开发者带来面试经历,源码解析以及技术分享,欢迎大家订阅。

欢迎关注,回复【教程】获取最新教学资源视频,回复【pdf】获取前端开发热门书籍pdf。

技术胖Redux免费教程 2万字长文章  (二)

微信扫码关注

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组件改成无状态组件可以提高程序性能,具体来看一下如何编写。

  1. 首先我们不在需要引入React中的{ Component },删除就好。
  2. 然后些一个TodoListUI函数,里边只返回JSX的部分就好,这步可以复制。
  3. 函数传递一个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
复制代码

经过上面的三个命令,应该可以在浏览器中出现下面的界面(出现画面说明我们项目初始化完成)。

技术胖Redux免费教程 2万字长文章  (二)

安装完成后,删除一些没有必要的样式和代码,在/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,请小伙伴们持续关注博客,获得的最新的学习视频。

★《布宫号》提醒您:民俗信仰仅供参考,请勿过度迷信!

本文经用户投稿或网站收集转载,如有侵权请联系本站。

发表评论

0条回复