React的AJAX最佳实践

原文:React AJAX Best Practices - Andrew H Farmer

引言

当你开始问AJAX和React的时候,大神们告诉的你的第一件事是React是一个视图层的库而且React没有网络/AJAX的功能特性。

了解这点虽然是应该的,但在你想要从服务器获取的数据放到React组件中的时候却并没有什么用。

实际上,实现这个的方式有很多。你自己说不定就有好几种想法,但是如果你选择了一种错误的方法,你的代码可能会很混乱。

所以你可能会想:哪种方式是『对的』或者『更优的』。

从服务器获取数据到React组件中的最佳实践是什么?

答案是…看具体情况。

四种方法

我收集了四种不错的实现AJAX和React的方法。

使用哪种方式取决于你的应用的大小和复杂度,以及你已经在使用的是哪些库和技术。

1.根组件 2.容器组件 3.Redux异步Action 4.Relay

根组件

这种方式最简单所以很适合原型构建和小型应用。

使用这种方法的时候,你需要创建一个根/父组件来管理所有AJAX请求。根组件在自己的状态中保存了所有AJAX响应数据,并且将状态(或者状态的一部分)作为属性传递到子组件中。

关于这种方式的例子可以查看React官方教程。CommentBox组件用于发所有AJAX请求。

译者注:作者写本文的时候React官方教程还是Thinking in React,现在官网已经更新了新的教程,新的教程中不含AJAX操作,详细请查阅文末的参考3

官方教程中我不喜欢的一个地方是他们使用了jQuery来发送AJAX请求。jQuery是一个拥有诸多功能特性的大型库,所以只用它做AJAX是毫无道理的。

我推荐使用fetch()函数。它是一个简单的,标准的,JavaScript AJAX API。Chrome和Firefox已经支持这个函数,在node和其他浏览器中也可以通过polyfill实现。想要选择自己得AJAX库,或者想要了解fetch()的详情,请看我的文章AJAX库的比较

另一个坑:如果你有一个较深的组件树(子组件的子组件的子组件…),那么你将要把数据传递得很长很长,从根组件到更深的组件。

什么时候使用根组件:

  • 你的组件树比较浅。
  • 你没有在用Redux或者flux。

容器组件

一个容器组件用于为展现组件(presentational conponents)或者其他容器组件提供数据和行为。如果你还没听过这个词,我推荐Dan Abramov的『展现和容器组件』

对于我们的目的来说,容器组件这种方式跟根组件方式一样,只是容器组件方式有多个组件和服务器进行交互。

工作原理如下:对于每个需要服务器数据的展现组件,创建一个发送AJAX请求的容器组件以获取数据,并通过属性为子组件提供数据。

举个实用的例子,假设你要用用户名和图片来展示一个用户的信息。

首先创建一个接收name和profileImage属性的UserProfile的展示组件。该组件不应该有任何AJAX代码。

然后创建一个接收userId属性的UserProfileContainer组件。它为特定的用户下载数据并通过属性传递给UserProfile

在容器组件中的AJAX请求可以通过简单的AJAX库发送。我推荐fetch()

什么时候使用容器组件做AJAX:

  • 你有一个很深的组件树。
  • 你的部分组件需要从服务器获取数据,但大多数组件都不需要。
  • 你在从多个API获取数据。
  • 你没有在用Redux/flux,或者你倾向于使用容器组件来做『异步动作』。

Redux异步Action

Redux管理数据,而AJAX从服务器提供数据,所以由Redux代码应该管理你的网络请求是有道理的。

如果你使用Redux,那别把AJAX放在你的React组件中,而应该放到你的异步Action中。

我推荐使用fetch()函数来处理实际网络请求,而正好,Redux官方文档中也是用的fetch()。官方文档中甚至用Redux、React和fetch()写了一个reddit示例API。

如果你在用flux,那跟这种方式类似,在你的action中发送网络请求。

什么是偶使用Redux异步Actions:

  • 如果你在使用Redux,这就是你在找的方法。
  • 如果你在使用flux,你的方式应该跟这个很类似。

Relay

如果使用Relay,就要用GraphQL声明React组件的数据需求,Relay会自动下载数据并填充到组件的属性中。

Relay很适应大型应用来的场景,但是需要需要更多的准备成本。你需要:

  • 学习Relay和GraphQL。
  • 使用GrahpQL指定React组件的数据需求(而不是propTypes)。
  • 设置一个GraphQL服务器。

Relay只负责和GraphQL服务器进行通信,所以它不会和任何三方API进行通信。

目前Relay只能和单一GraphQL服务器通信,所以如果你在从多个数据源获取数据,这种方法就不适合。和多个数据源通信的特性应该会在后续版本中添加,相关情况在这个GitHub Issue中有讨论。

如果你打算使用这种方式,Relay Playground是一个用来搞清楚Relay是怎么工作的的一个好地方。

什么时候使用Relay:

  • 构建一个大型应用,并且正在担忧的问题正好是Relay被设计出来解决的相关的问题。
  • 你还没有一个已经构建好的JSON API。
  • 你打算设置一个GraphQL服务器。
  • 你的应用只和单台服务器进行通信。

附赠:反模式(Anti-patterns)

如果上述的所有方法都是符合需求的,那哪些不应该选择呢?我建议一般应该避免这两种方式。

反模式1:在展现组件中的AJAX请求

不要在一个已经有其他功能的组件中实现AJAX逻辑,比如一个在做复杂接口的渲染组件。这会破坏关注分离(Separation of concerns)的设计原则。

反模式2:ReactDOM.render()

你可能有在整个React逻辑外面写了AJAX逻辑并且在当获取了服务器的更新数据时调用ReactDOM.render()

这种方式可能没有问题。我不建议这么做是因为我认为根组件模式跟这种做法相似,并且更简洁。

总结

使用React不是Rails或者Angular,React构建的应用是多模块的(Modular),React只是其中的一个模块,AJAX库是另一个模块。

你也许会需要了解一些其他模块,以及怎样把他们整合并适应在一起。

做XXX的最好的库是什么?怎样整合YYY和React?我的博客会经常更新,并主要就是回答这类问题。

参考

  1. React AJAX Best Practices - Andrew H Farmer
  2. Thinking in React - React
  3. Tutorial: Intro To React - React
  4. AJAX/HTTP Library Comparison - Andrew H Farmer
  5. Presentational and Container Components - Dan Abramov
  6. Async Actions - Redux Document
  7. Example: Reddit API - Redux Document
  8. Relay - Facebook
  9. Why invent something new? - GraphQL Introduction - Facebook
  10. Separation of concerns - Wikipedia