深入浅出React之第六章:Redux和服务器通信
来源:互联网 发布:lol mac国服怎么取消了 编辑:程序博客网 时间:2024/06/06 03:22
无论是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之第六章:Redux和服务器通信
- 实操《深入浅出React和Redux》第四期--react-redux
- 深入浅出React之第三章:使用redux管理应用状态
- 深入浅出React之第四章:推荐的Redux目录结构
- 实操《深入浅出React和Redux》第三期—Redux
- react知识小结——深入浅出React和Redux
- 实操《深入浅出React和Redux》第一期
- 实操《深入浅出React和Redux》第二期—Flux
- React-Native 之 redux 与 react-redux
- React-Native 之 redux 与 react-redux
- React和Redux的连接react-redux
- React和Redux的连接react-redux
- React和Redux的连接react-redux
- React和Redux的连接react-redux
- React和Redux的连接react-redux
- React和Redux的连接react-redux
- React和Redux的连接react-redux
- React Native 之 Redux
- Linux下进程的创建过程分析(_do_fork/do_fork详解)--Linux进程的管理与调度
- java int型转char型
- 深入浅出React之第五章:React组件的性能优化
- md5解密技术
- 验证码绘制
- 深入浅出React之第六章:Redux和服务器通信
- 深入浅出React之第七章:使用React-Router实现多页面应用
- 简单回答单点登录的三种方式
- 五. PyQuery
- while opening read connection: jdbc:mysql message from server: Host is not allowed to connect to t
- linux练习
- LINUX下使用elasticsearch-jdbc工具实现MySQL同步到ElasticSearch 以及linux 64位centos系统安装jdk1.8
- Jump Game
- layer弹出层