首页 男生 其他 深入浅出React和Redux

7.1.2 React组件访问服务器的生命周期

深入浅出React和Redux 程墨 6068 2021-04-06 02:29

  您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!

  

  7.1.2 React组件访问服务器的生命周期

  在weather_react应用中,我们创造一个名为Weather的组件,这个组件可以在本书的Github代码库https://github.com/mocheng/react-and-redux/的chapter-07/weather_react目录下找到。

  这个Weather组件将要显示指定城市的当前天气情况,这个组件封装了两个功能:

  ·通过服务器API获得天气情况数据;

  ·展示天气情况数据。

  现在面临的首要问题是如何关联异步的网络请求和同步的React组件渲染。

  访问服务器API是一个异步操作。因为JavaScript是单线程的语言,不可能让唯一的线程一直等待网络请求的结果,所以所有对服务器的数据请求必定是异步请求。

  但是,React组件的渲染又是同步的,当开始渲染过程之后,不可能让Weather组件一边渲染一边等待服务器的返回结果。

  总之,当Weather组件进入装载过程的时候,即使此时Weather立刻通过fetch函数发起对服务器的请求,也没法等待服务器的返回数据来进行渲染。因为React组件的装载过程和更新过程中生命周期函数是同步执行的,没有任何机会等待一个异步操作。

  所以,可行的方法只能是这样,分两个步骤完成:

  步骤1,在装载过程中,因为Weather组件并没有获得服务器结果,就不显示结果。或者显示一个“正在装载”之类的提示信息,但Weather组件这时候要发出对服务器的请求。

  步骤2,当对服务器的请求终于获得结果的时候,要引发Weather组件的一次更新过程,让Weather重新绘制自己的内容,这时候就可以根据API返回结果绘制天气信息了。

  从上面的过程可以看得出来,为了显示天气信息,必须要经历装载过程和更新过程,至少要渲染Weather组件两次。

  还有一个关键问题,在装载过程中,在什么时机发出对服务器的请求呢?

  通常我们在组件的componentDidMount函数中做请求服务器的事情,因为当生命周期函数componentDidMount被调用的时候,表明装载过程已经完成,组件需要渲染的内容已经在DOM树上出现,对服务器的请求可能依赖于已经渲染的内容,在component-DidMount函数中发送对服务器请求是一个合适的时机。

  另外,componentDidMount函数只在浏览器中执行,在第12章介绍同构时,我们会介绍React在服务器端渲染的过程,当React组件在服务器端渲染时,肯定不希望它发出无意义的请求,所以componentDidMount是最佳的获取初始化组件内容请求的时机。

  万事俱备,我们来看一看定义Weather组件的weather.js文件内容,首先是构造函数,代码如下:

  constructor() {

  super(...arguments);

  this.state = {weather: null};

  }

  因为Weather组件要自我驱动更新过程,所以Weather必定是一个有状态的组件,状态中包含天气情况信息,在状态上有一个weather字段,这个字段的值是一个对象,格式和服务器API返回的JSON数据中的weatherinfo字段一致。

  在render函数中,所要做的是渲染this.state上的内容,代码如下:

  render() {

  if (!this.state.weather) {

  return 暂无数据;

  }

  const {city, weather, temp1, temp2} = this.state.weather;

  return (

  {city} {weather} 最低气温 {temp1} 最高气温 {temp2}

  )

  }

  在构造函数中,我们将组件状态上的weather字段初始化为null。这样,在装载过程引发的第一次render函数调用时,就会直接渲染一个“暂无数据”的文字;但是当state上包含weather信息时,就可以渲染出实际的天气信息。

  通过API获得数据的工作交给componentDidMount,代码如下:

  componentDidMount() {

  const apiUrl = `/data/cityinfo/${cityCode}.html`;

  fetch(apiUrl).then((response) => {

  if (response.status !== 200) {

  throw new Error('Fail to get response with status ' + response.status);

  }

  response.json().then((responseJson) => {

  this.setState({weather: responseJson.weatherinfo});

  }).catch((error) => {

  this.setState({weather: null});

  });

  }).catch((error) => {

  this.setState({weather: null});

  });

  }

  fetch函数执行会立刻返回,返回一个Promise类型的对象,所以后面会跟上一大串then和catch的语句。每个Promise成功的时候,对应的then中的回调函数会被调用;如果失败,对应catch中的回调函数也被调用。

  值得注意的是,fetch的参数apiUrl中只有URL的路径部分,没有协议和域名部分,代码如下:

  const apiUrl = `/data/cityinfo/${cityCode}.html`;

  这样是为了让fetch根据当前网页的域名自动配上协议和域名,如果当前网页地址是http://localhost,那么fetch请求的URL就是http://localhost/data/cityinfo/101010100.html;如果当前网页地址是http://127.0.0.1,那么URL就是http://127.0.0.1/data/cityinfo/101010100.html,好处就是网页代码中无需关心当前代码被部署在什么域名下。

  遗憾的是,中国天气网没有提供根据访问者IP映射到城市的功能。在这个例子中我们硬编码了北京市的城市代码,读者在实际操作的时候,也可以把weather.js中的模块级变量cityCode改为你所在的城市,在http://www.weather.com.cn/页面上搜索城市名,网页跳转后URL上的数字就是城市对应的城市代码。

  componentDidMount中这段代码看起来相当繁杂。不过没有办法,输入输出操作就是这样,因为fetch的过程是和另一个计算机实体通信,而且通信的介质也是一个无法保证绝对可靠的互联网,在这个通信过程中,各种异常情况都可能发生,服务器可能崩溃没有响应,或者服务器有响应但是返回的不是一个状态码为200的结果,再或者服务器返回的是一个状态码为200的结果,结果的实际内容可能并不是一个合法的JSON数据。正因为每一个环节都可能出问题,所以每一个环节都需要判断是不是成功。

  虽然被fetch广为接受,大有取代其他网络访问方式的架势,但是它有一个特性一直被人诟病,那就是fetch认为只要服务器返回一个合法的HTTP响应就算成功,就会调用then提供的回调函数,即使这个HTTP响应的状态码是表示出错了的400或者500。正因为fetch的这个特点,所以我们在then中,要做的第一件事就是检查传入参数response的status字段,只有status是代表成功的200的时候才继续,否则以错误处理。

  当response.status为200时,也不能直接读取response中的内容,因为fetch在接收到HTTP响应的报头部分就会调用then,不会等到整个HTTP响应完成。所以这时候也不保准能读到整个HTTP报文的JSON格式数据。所以,response.body函数执行并不是返回JSON内容,而是返回一个新的Promise,又要接着用then和catch来处理成功和失败的情况。如果返回HTTP报文内容是一个完整的JSON格式数据就会成功,如果返回结果不是一个JSON格式,比如是一堆HTML代码,那就会失败。

  当历经各种检查最后终于获得了JSON格式的结果时,我们通过Weather组件的this.setState函数把weatherinfo字段赋值到weather状态上去,如果失败,就把weather设为null。

  处理输入输出看起来的确很麻烦,但是必须要遵照套路把所有可能出错的情况都考虑到,对任何输入输出操作只要记住一点:不要相信任何返回结果。

  至此,Weather功能完成了,我们打开网页刷新察看最终效果,可以看到网页最开始显示“暂无数据”,这是装载过程的渲染结果,过了一会,当通过代理调用中国天气网远端API返回的时候,网页上就会显示北京市的天气情况,这是API返回数据驱动的Weather组件更新过程的渲染结果,显示界面如图7-1所示。

  图7-1 组件Weather的界面 深入浅出React和Redux

目录
设置
手机
书架
书页
评论