您的位置:首页 > Web前端 > React

深入浅出React之第六章:Redux和服务器通信

2017-10-09 00:42 281 查看
无论是
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: