深入浅出React之第六章:Redux和服务器通信
2017-10-09 00:42
281 查看
无论是
1.
我们先来看一下一些比较简单的场景,在一些比较简单的应用中,我们可能只需要使用
有很多JavaScript框架都支持和服务器进行通信,例如最传统的
一个趋势是在
1.1
首先,访问服务器api是一个异步操作。而
但是,
所以,我们一般这样去处理一个组件和服务器的通信:
步骤 1 :在装载过程中,由于组件没有获取服务器结果,就不显示结果,或者显示一个
步骤 2:当对服务器的请求获取结果时,引发组件的一次更新过程,让组件重新绘制自己的内容,显示服务器数据。
从上面过程可以看出,为了完成一次组件和服务器之间的通信,必须要经历装载和更新过程,至少要渲染一个组件两次。
那么,在装载过程中,在什么时机发出对服务器的请求呢?
通常,我们在组件的
示例代码如下:
虽然
当
函数执行并不是返回
1.2
优点:简单直接,容易理解。
缺点:把状态存放在组件中实在不是一个很好的选择,尤其是当组件变得庞大复杂了之后。
2.
使用
驱动
按照
在
异步
当我们想要让
如果没有
不过,由于
异步操作的模式
有了
一个访问服务器的
表示异步操作已经开始的
表示异步操作成功的
表示异步操作失败的
当这三种类型的
异步操作正在进行中
异步操作已经成功完成
异步操作已经失败
下面,我们来看一下
异步
React还是
Redux,工作方式都是依靠数据驱动,在开发过程中,应用数据往往存储在数据库中,通过一个api服务器暴露出来,网页应用要获取数据,就需要与服务器进行通信。
1.React
组件访问服务器
我们先来看一下一些比较简单的场景,在一些比较简单的应用中,我们可能只需要使用react,而不使用
redux之类的数据管理框架,这时候
react组件自身也可以担当起和服务器通信的职责。
有很多JavaScript框架都支持和服务器进行通信,例如最传统的
jQuery的
$.ajax()函数。但是既然我们都已经使用了
react,就没有必要再为了一个
ajax函数在引入
jQuery这一个框架。
一个趋势是在
react应用中使用浏览器原生支持的
fetch函数来访问网络资源,
fetch函数返回的结果是一个
Promise对象,
Promise作为一个新推出的api,能够让异步处理的代码更加简洁清晰。现代浏览器大部分都已经原生支持了
fetch函数,对于不支持
fetch的浏览器版本,也可以通过
fetch的
polyfill来增加对
fetch的支持。这个
polyfill相当于是给全局的
window对象上增加了一个
fetch函数,让这个网页中的
javascript可以直接使用
fetch函数。
1.1 React
组件访问服务器的生命周期
首先,访问服务器api是一个异步操作。而javascript是单线程语言,不可能让主线程一直等待网络请求的结果。所以,所有对服务器的数据请求必定是一个异步操作。
但是,
React组件的渲染又是同步的,当开始渲染过程之后,不可能让组件一边渲染一边等待服务器的返回结果。
所以,我们一般这样去处理一个组件和服务器的通信:
步骤 1 :在装载过程中,由于组件没有获取服务器结果,就不显示结果,或者显示一个
loading之类的提示信息。与此同时,组件发出对服务器的数据请求。
步骤 2:当对服务器的请求获取结果时,引发组件的一次更新过程,让组件重新绘制自己的内容,显示服务器数据。
从上面过程可以看出,为了完成一次组件和服务器之间的通信,必须要经历装载和更新过程,至少要渲染一个组件两次。
那么,在装载过程中,在什么时机发出对服务器的请求呢?
通常,我们在组件的
componentDidMount函数中做请求服务器的事情。因为当生命周期函数
componentDidMount被调用时,表明装载过程已经完成,组件需要渲染的内容已经在DOM树上出现,对服务器的请求可能依赖于已经渲染的内容,在
componentDidMount函数中发送对服务器请求是一个合适的时机。
示例代码如下:
import React,{Component} from 'react'; class Weather extends Component{ constructor(props){ super(props); this.state={ info:null } } componentDidMount(){ const apiUrl=`http://localhost:7760/service/xxx`; fetch(apiUrl).then((resp)=>{ if(resp.status!==200) throw new Error('fail to get response with status:'+resp.status); resp.json().then((respJson)=>{ this.setState({ info:respJson.info }); }).catch((err)=>{ this.setState({ info:null }); }) }).catch((err)=>{ this.setState({ info:null }); }) } render(){ if(!this.state.info) return <div>暂无数据</div> const {info}=this.state; return ( <div>{info}</div> ) } }
虽然
fetch现在被广为接受,但是它有一个特性一直被人诟病,那就是
fetch认为只要服务器返回一个合法的
http响应就算成功,就会调用
then提供的回调函数,即使这个
http响应的状态码是表示出错的400或者500。所以,我们在
then中,要做的第一件事就是检查传入参数
resp的
status字段,只有
status是代表成功的200的时候才继续,否则以错误处理。
当
resp.status为200时,也不能直接读取
resp的内容,因为
fetch在接收到
HTTP响应的报头部分就会调用
then,不会等到整个
HTTP响应完成,所以这时候也不能保证能读到整个
HTTP报文的
JSON格式数据。所以,
resp.body
函数执行并不是返回
json内容,而是返回一个新的
Promise,又要接着用
then和
catch来处理成功和失败的情况。
1.2 React
组件访问服务器的优缺点
优点:简单直接,容易理解。缺点:把状态存放在组件中实在不是一个很好的选择,尤其是当组件变得庞大复杂了之后。
Redux是用来帮助管理应用状态的,应该尽量把状态存放在
Redux Store中,而不是放在
React组件中。
2. Redux
访问服务器
使用redux访问服务器,同样要解决的是异步问题。我们这里使用
Redux-thunk来处理
redux中的异步操作。
redux单向数据流同步操作流程
驱动
redux流程的是
action对象,每一个
action对象被派发到
store上之后,同步的被分配到所有的
reducer函数,每个
reducer都是纯函数,不会产生任何的副作用,完成数据操作之后立刻同步返回,
reducer返回的结果又被拿去更新
store上的状态数据,更新状态数据的操作会立刻被同步给监听
store状态改变的函数,从而引发
react视图组件的更新。
redux-thunk中间件
按照
redux-thunk的想法,在
redux单向数据流中,在
action对象被
reducer函数处理之前,是插入异步功能的时机。
在
redux架构下,一个
action对象在通过
store.dispatch派发,在调用
reducer函数之前,会经过一个中间件的环节,这里就是产生异步操作的机会。
异步
action对象
当我们想要让
redux帮忙处理一个异步操作的时候,代码一样要派发一个
action对象,毕竟
redux单向数据流就是由
action对象驱动的。但是这个
action对象比较特殊,我们叫他
异步action对象。这个异步
action对象可不是一个普通的
javascript对象,而是一个函数。
如果没有
redux-thunk中间件的存在,这样一个函数类型的
action对象会被派发到各个
reducer函数,
reducer函数从这些实际上是函数的
action对象上是无法获取
type字段的,所以也做不了什么实质性的处理。
不过,由于
redux-thunk这个中间件的存在,这些
action对象根本没机会接触到
reducer函数,在中间件一层就会被
redux-thunk截获。
redux-thunk的工作是检查
action对象是不是函数,如果不是函数就放行,完成普通的
action对象的生命周期,如果是函数,则执行这个函数,并把
store的
dispatch函数和
getState函数作为参数传递到函数中去,处理过程到此为止,不会让这个异步
action对象继续往前派发到
reducer函数。
action对象函数中完全可以通过
fetch发起一个对服务器的异步请求,当得到服务器结果之后,通过参数
dispatch,把失败或者成功的结果当做
action对象派发出去,由于这一次派发的是一个普通的
action对象,因此不会被
redux-thunk截获,而是直接派发到
reducer,驱动
store上状态的改变。
异步操作的模式
有了
redux-thunk的帮助,我们可以使用异步
action对象来完成异步访问服务器的功能了。在此之前,我们先想一想如何设计
action类型和视图。
一个访问服务器的
action,至少要涉及三个
action类型:
表示异步操作已经开始的
action类型
表示异步操作成功的
action类型
表示异步操作失败的
action类型
当这三种类型的
action对象被派发时,会让
react组件进入各自不同的三种状态:
异步操作正在进行中
异步操作已经成功完成
异步操作已经失败
下面,我们来看一下
action构造函数如何定义:
import {FETCH_STARTED,FETCH_SUCCESS,FETCH_ERROR} from './actionTypes.js'; export const fetchStarted=()=>({ type:FETCH_STARTED }); export const fetchSuccess=(result)=>({ type:FETCH_SUCCESS, result }); export const fetchError=(error)=>({ type:FETCH_ERROR, error }); export const fetchAction=(url)=>{ return (dispatch)=>{ dispatch(fetchStarted()); fetch(url).then((resp)=>{ if(resp.status!==200) throw new Error('there is an error,resp status is :'+resp.status); resp.json().then((respJson)=>{ dispatch(fetchSuccess(respJson.info)); }).catch((error)=>{ throw new Error('Invalid Json resp:'+error); }) }).catch((error)=>{ dispatch(fetchError(error)); }) } }
异步
action构造函数的模式就是 函数体内返回一个新的函数,这个新的函数可以有两个参数
dispatch和
getState。分别代表
redux唯一
store上的成员函数
dispatch和
getState。
相关文章推荐
- react知识小结——深入浅出React和Redux
- 深入浅出React+Redux(六:模块化 React 和 Redux 应用)
- 深入浅出React+Redux(三:Flux单向数据流,相关代码在github flux分支)
- 深入浅出React和Redux
- 深入浅出React+Redux(二:React 组件的生命周期)
- 深入浅出React+Redux(四:Redux - 组件的Context)
- 深入浅出React之第三章:使用redux管理应用状态
- 深入浅出React之第四章:推荐的Redux目录结构
- 深入浅出React+Redux(五:React-Redux)
- 我是如何使用React+Redux构建大型应用的
- MySQL客户端/服务器通信协议
- 对React-redux中connect方法的理解
- java NIO 服务器与多客户端双向通信 非阻塞
- 两部android设备通过服务器转发实现通信简单demo
- 客户端通过TCP通信分页从服务器获取数据
- [10] Window PowerShell DSC 学习系列----目标节点和Pull 服务器底层通信原理剖析
- 深入浅出讲解:php的socket通信_0
- 初探基于TCP的服务器/客户端结构的聊天系统(二)之应用层通信协议设计
- react项目学习笔记四(redux和redux的中间件redux-thunk的认识)
- React-Redux总结1