react 可以说是最流行的用于构建交互式 Web 应用程序的库。然而,React 并不是一个成熟的网络框架。它侧重于古老的 MVC 模型的视图部分。
有一个完整的 React 生态系统可以解决其他方面的问题。在本教程中,您将了解任何 Web 应用程序的最基本元素之一——如何获取要显示的数据。这不是微不足道的。React 组件层次结构中有几个地方可以获取数据。何时获取数据是另一个问题。您还需要考虑使用什么技术来获取数据以及将其存储在哪里。
在本教程结束时,您将清楚地了解 React 中数据获取的工作原理、不同方法的优缺点,以及如何将这些知识应用到您的 React 应用程序中。
入门
让我们使用create-react-app为我们的 React 应用程序创建一个骨架:
> create-react-app react-data-fetcher
结果是一个非常复杂的目录结构。 如果您不熟悉 create-react-app,请阅读出色的 README文件。
创建一个简单的服务器
我创建了一个简单的服务器来存储和提供报价。它不是本教程的重点,它的作用是提供一个远程api来演示如何使用 React 获取数据。只是为了满足你的好奇心,它是一个基于hug框架并使用 redis 作为持久存储 的python 3 应用程序。
API 非常简单。有一个端点, /quotes
。它返回所有存储的报价以响应 HTTP GET 请求,您可以通过发送 HTTP发布请求来添加新报价。
完整的源代码可在GitHub 上获得。
演示应用概述
演示应用程序是一个 React 应用程序,它与报价服务进行通信,显示所有报价,并允许您添加新报价。
这是一个屏幕截图:
应用程序结构非常简单。我从 create-react-app 创建的骨架开始,并在 src 子目录中添加了两个组件:QuoteList 和 AddQuoteForm。这是目录结构(不包括节点_modules):
~/git/react-data-fetcher > tree -I node_modules -L 2 . ├── README.md ├── README2.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── AddQuoteForm.css│ ├── AddQuoteForm.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── QuoteList.js │ ├── index.css │ ├── index.js │ └── registerServiceWorker.js └── yarn.lock 2 directories, 16 files
GitLab上提供了完整的源代码。
显示报价
QuoteList 功能组件将引用列表显示为项目符号列表。它需要一个字符串数组:
import React from 'react' const QuoteList = ({quotes}) => quotes.map(quote => <li key={quote}>{quote}</li>) export default QuoteList
它是主 App 组件的子组件。
使用 Fetch API 获取数据
fetch API 是一个基于 promise 的 API,它返回一个响应对象。为了获取实际的 JSON 内容,您需要调用json()
响应对象的方法。
fetchQuotes = () => { this.setState({...this.state, isFetching: true}) fetch(QUOTE_SERVICE_URL) .then(response => response.json()) .then(result => this.setState({quotes: result, isFetching: false})) .catch(e => console.log(e)); } }
放置您的数据获取代码
当然,React 完全是关于组件的。在哪里放置数据获取代码的问题很重要。如果你很好地考虑你的代码,你将拥有很多通用组件和一些特定于应用程序的组件。React 和javascript通常非常灵活,因此可以在任何地方注入逻辑。
从rest API获取报价需要某种形式的轮询,因为我希望报价始终是最新的。但初始获取也很重要。React 组件具有生命周期方法,您可以在其中实现将在特定时间执行的逻辑。componentDidMount()
当可以访问组件并修改其状态时,该方法会触发。这是启动数据获取的理想场所。
这是它的样子:
componentDidMount() { this.fetchQuotes() }
如果你真的想减少第一次查看的时间,你可以考虑使用componentWillMount()
来启动你的异步获取,但是你有风险在组件安装之前完成获取。我不推荐这种方法。
查看 掌握 React 生命周期方法 以获取更多详细信息。
选择获取数据的频率
最初的获取componentDidMount()
很棒,但我想经常更新报价。在基于 REST 的 API 中,唯一的解决方案是定期轮询服务器。报价服务非常基础,总是返回所有报价。
更具可扩展性的服务将提供一种检查更新甚至使用 HTTP if-modify-since 或 eTag 的方法。componentDidMount()
我们的演示应用程序只是通过启动计时器并清理每五秒获取所有内容componentWillUnmount()
:
componentDidMount() { this.fetchQuotes() this.timer = setInterval(() => this.fetchQuotes(), 5000); } componentWillUnmount() { this.timer = null; }
轮询持续时间是特定于应用程序的决定。如果您需要实时更新和/或轮询对后端的压力太大,请考虑使用 WebSockets 而不是 REST。
处理长时间运行的数据获取
有时数据获取可能需要很长时间。在这种情况下,显示进度条或闪亮的动画让用户知道正在发生的事情会对用户体验做出很大贡献。当用户启动数据获取(例如通过单击搜索按钮)时,这一点尤其重要。
在演示应用程序中,我只是在获取过程中显示一条消息“获取报价...”。在render()
主 App 组件的方法中,我通过检查state.isFetching
成员来利用条件渲染。
render() { const title = 'Quotes for ya!' let now = new Date() return ( <div className='App'> <h2 className='App-title'>{title}</h2> <p>{this.state.isFetching ? 'Fetching quotes...' : ''}</p> <QuoteList quotes={this.state.quotes} /> <AddQuoteForm quote_service_url={QUOTE_SERVICE_URL}/> </div> ); }
该fetchQuotes()
方法state.isFetching
通过在启动时将其初始化为 true 并在接收到引号时将其设置回 false 来处理更新:
fetchQuotes = () => { this.setState({...this.state, isFetching: true}) fetch(QUOTE_SERVICE_URL) .then(response => response.json()) .then(result => this.setState({quotes: result, isFetching: false})) .catch(e => console.log(e)); } }
处理错误
我在这里通过将捕获的错误记录到控制台来做最少的错误处理。根据您的应用程序,您可能会调用一些重试逻辑、通知用户或显示一些后备内容。
Fetch API 与 Axios
fetch API 有几个问题。它需要从响应中提取 JSON 的额外步骤。它也不会捕获所有错误。例如,404 将作为正常响应返回。您必须检查响应代码并处理被捕获的网络错误。
所以你必须在两个地方处理错误。但是您可以使用axios.js 库来解决这些问题,并以添加外部依赖项为代价获得更简洁的代码。以下是使用 axios 的代码:
fetchQuotes = () => { this.setState({...this.state, isFetching: true}) axios.get(QUOTE_SERVICE_URL) .then(response => this.setState({quotes: response.data, isFetching: false})) .catch(e => console.log(e); }
这看起来不多,但它有帮助。添加新报价的代码使用 axios 更加简洁。这是获取版本:
handleSubmitWithFetch = event => { let data = new FormData() data.append('quote', this.state.quote) fetch(this.props.quote_service_url, {method: 'post', body: data}) .then(response => response.json()) .catch(e => console.log(e)); event.preventDefault(); }
这是 axios 版本:
handleSubmit = event => { axios.post(this.props.quote_service_url, {'quote': this.state.quote}) .then(r => console.log(r)) .catch(e => console.log(e)); event.preventDefault(); }
结论
在本教程中,您学习了如何在 React 应用程序中异步获取数据。我们讨论了相关的生命周期方法、轮询、进度报告和错误处理。
我们查看了两个基于 Promise 的库:fetch API 和 axios.js。现在,去那里构建访问远程 API 的很棒的 React 应用程序。