原文: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?我的博客会经常更新,并主要就是回答这类问题。
参考
- React AJAX Best Practices - Andrew H Farmer
- Thinking in React - React
- Tutorial: Intro To React - React
- AJAX/HTTP Library Comparison - Andrew H Farmer
- Presentational and Container Components - Dan Abramov
- Async Actions - Redux Document
- Example: Reddit API - Redux Document
- Relay - Facebook
- Why invent something new? - GraphQL Introduction - Facebook
- Separation of concerns - Wikipedia