React Native对于希望轻松构建移动应用的开发者来说是一个了不起的库。它提供了一种向前端显示信息的有效方式。但我们如何获得数据,以便我们的组件能够渲染它?
在这篇文章中,你将学习如何从API中获取数据并将其显示给用户。我们将用全面的代码样本介绍几种方法,以帮助你确定适合你的应用程序的最佳方法。
我们将介绍在React Native中获取数据的下列选项:
- 使用内置的Fetch API
- 使用Apisauce获取数据
- 使用渲染道具来渲染数据
- 使用GraphQL和Apollo客户端获取数据
- 用类组件获取数据
为了展示React Native中的数据采购,我们将构建一个基本的应用程序,从Coffee API中获取一个项目列表。此外,我们将使用NativeBase UI库来向客户端渲染我们的数据。
最后,你的示例应用程序将看起来像这样。
你可以从这个GitHub仓库获得这个应用程序的完整源代码。
开始使用
项目初始化
要用Expo搭建一个React Native项目,请运行以下终端命令。
expo init reactnative-data-fetching
获取依赖性
在这里,我们将安装以下模块:
@apollo/client:用于进行GraphQL查询graphql:Apollo客户端的同行依赖性native-base,styled-components,styled-system: 用于使用NativeBase库
要获得这些包,请编写以下终端命令。
npm i @apollo/client graphql native-base styled-components styled-system
作为下一步,像这样安装NativeBase的对等依赖。
expo install react-native-svg
expo install react-native-safe-area-context
完成后,是时候演示一下数据的获取了。
你什么时候需要获取数据?
有三个原因你会需要获取数据:
- 在组件的第一次渲染时加载数据
- 当用户点击一个按钮时,获取数据并进行渲染
- 在不同的时间间隔内加载数据
我们将为这些用例分别编写代码。
使用内置的Fetch API
Fetch API是检索数据的最常用方法,因为它与React捆绑在一起。
挂载的数据获取
在你的components 文件夹中,创建一个名为CoffeeAutonomous.js 的文件。在那里,开始写下面的代码。
import React, { useState, useEffect } from "react";
import { Box, FlatList, Center, NativeBaseProvider, Text } from "native-base";
export default function CoffeeAutonomous() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const fetchData = async () => {
const resp = await fetch("https://api.sampleapis.com/coffee/hot");
const data = await resp.json();
setData(data);
setLoading(false);
};
const renderItem = ({ item }) => {
return (
<Box px={5} py={2} rounded="md" bg="primary.300" my={2}>
{item.title}
</Box>
);
};
让我们一块一块地剖析这段代码。
在开始时,我们创建了两个Hooks,叫做data 和loading 。data 钩子将保存取来的数据,loading 将告诉用户数据是否在路上。
此外,fetchData 方法将使用fetch 方法从服务器获得响应,然后将其存储到data Hook中。最后,我们将loading Hook设置为false 。除此以外,renderItem 函数将显示每个项目的title 字段。
现在我们需要渲染它。要做到这一点,在同一文件中添加以下代码。
useEffect(() => {
fetchData();
}, []);
return (
<NativeBaseProvider>
<Center flex={1}>
<Box> Fetch API</Box>
{loading && <Box>Loading..</Box>}
{data && (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
)}
</Center>
</NativeBaseProvider>
);
}
注意,我们将useEffect 依赖数组留空。这意味着React将在第一次渲染时调用fetchData 方法。接下来,我们用FlatList 组件来显示data 数组中的内容。
最后,转到App.js ,渲染CoffeeAutonomous 组件。
import React from "react";
import CoffeeAutonomous from "./components/CoffeeAutonomous";
export default function App() {
return <CoffeeAutonomous />;
}
这将是输出结果:
在下一节,你将学习如何在用户点击按钮时渲染数据。
最后,CoffeeAutonomous.js 应该是这样的。
import React, { useState, useEffect } from "react";
import { Box, FlatList, Center, NativeBaseProvider, Text } from "native-base";
export default function CoffeeAutonomous() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const fetchData = async () => {
const resp = await fetch("https://api.sampleapis.com/coffee/hot");
const data = await resp.json();
setData(data);
setLoading(false);
};
useEffect(() => {
fetchData();
}, []);
const renderItem = ({ item }) => {
return (
<Box px={5} py={2} rounded="md" bg="primary.300" my={2}>
{item.title}
</Box>
);
};
return (
<NativeBaseProvider>
<Center flex={1}>
<Box> Fetch API</Box>
{loading && <Box>Loading..</Box>}
{data && (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
)}
</Center>
</NativeBaseProvider>
);
}
点击按钮时获取数据
创建一个名为CoffeeClick.js 的文件,并编写以下代码。
import React, { useState } from "react";
import { Box, FlatList, Center, NativeBaseProvider, Button } from "native-base";
export default function CoffeeClick() {
const [data, setData] = useState(null);
const [visible, setVisible] = useState(true);
const fetchData = async () => {
const resp = await fetch("https://api.sampleapis.com/coffee/hot");
const data = await resp.json();
setData(data);
setVisible(false);
};
const renderItem = ({ item }) => {
return (
<Box px={5} py={2} rounded="md" bg="primary.300" my={2}>
{item.title}
</Box>
);
};
}
这段代码的第一部分与CoffeeAutonomous 的代码相似。唯一的区别是,我们声明了一个visible Hook。除此之外,在fetchData 函数中,我们告诉React,如果数据现在已经存在,那么将visible Hook设置为false 。
为了渲染用户界面,附加以下代码。
return (
<NativeBaseProvider>
<Center flex={1}>
{visible && <Button onPress={() => fetchData()}>Press</Button>}
{data && (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
)}
</Center>
</NativeBaseProvider>
);
}
在第4行,我们使用了条件性渲染。这将在点击时隐藏该组件。
在下一节中,你将学习如何以固定的时间间隔获取数据。
我们的CoffeeClick.js 文件应该看起来像这样:
import React, { useState } from "react";
import { Box, FlatList, Center, NativeBaseProvider, Button } from "native-base";
export default function CoffeeClick() {
const [data, setData] = useState(null);
const [visible, setVisible] = useState(true);
const fetchData = async () => {
const resp = await fetch("https://api.sampleapis.com/coffee/hot");
const data = await resp.json();
setData(data);
setVisible(false);
};
const renderItem = ({ item }) => {
return (
<Box px={5} py={2} rounded="md" bg="primary.300" my={2}>
{item.title}
</Box>
);
};
return (
<NativeBaseProvider>
<Center flex={1}>
{visible && <Button onPress={() => fetchData()}>Press</Button>}
{data && (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
)}
</Center>
</NativeBaseProvider>
);
}
间隔性地获取数据
这个步骤很简单。创建一个名为CoffeeInterval.js 的文件。
之后,从CoffeeAutonomous.js 复制代码并粘贴。我们将进行修改以增加间隔功能。
在CoffeeInterval.js ,改变你的useEffect 处理程序。
useEffect(() => {
fetchData();
const dataInterval = setInterval(() => fetchData(), 5 * 1000);
return () => clearInterval(dataInterval);
}, []);
在这段代码中,我们使用setInterval 函数,每隔5 秒运行fetchData 方法。后来,我们指定,如果这个组件从树上被删除,那么就清除这个间隔。这将防止内存泄漏。
这就是全部的内容了!你的代码运行起来应该没有任何问题。
你的CoffeeInterval.js 文件应该看起来像这样:
import React, { useState, useEffect } from "react";
import { Box, FlatList, Center, NativeBaseProvider, Text } from "native-base";
export default function CoffeeInterval() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const fetchData = async () => {
const resp = await fetch("https://api.sampleapis.com/coffee/hot");
const data = await resp.json();
setData(data);
setLoading(false);
};
useEffect(() => {
fetchData();
const dataInterval = setInterval(() => fetchData(), 5 * 1000);
return () => clearInterval(dataInterval);
}, []);
const renderItem = ({ item }) => {
return (
<Box px={5} py={2} rounded="md" bg="primary.300" my={2}>
{item.title}
</Box>
);
};
return (
<NativeBaseProvider>
<Center flex={1}>
{loading && <Box>Loading..</Box>}
{data && (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
)}
</Center>
</NativeBaseProvider>
);
}
用Apisauce获取数据
Fetch的一个替代品是Axios。但由于Axios与React Native不兼容,我们可以用Apisauce代替。它是Axios的一个封装器,甚至可以让你进行POST 、PUT 、DELETE 的请求。
要安装该包,请运行这个终端命令:
npm i apisauce
用Apisauce获取数据的简单方法
这就是你如何用Apisauce库提出请求。
import React from "react";
import { useEffect } from "react";
import { create } from "apisauce";
//file name: SauceExample.js
//extra code removed for brevity purposes
//The baseURL will be our starting point.
const api = create({
baseURL: "https://api.sampleapis.com/coffee",
});
const fetchData = () => {
//make request to baseURL + '/hot'
api
.get("/hot")
.then((response) => response.data)
.then((data) => console.log(data));
};
useEffect(() => {
fetchData();
}, []);
最后,我们告诉React在第一次渲染时执行fetchData 函数。这将把API的响应记录到终端。
我们将使用Hooks来渲染这些数据。
使用Apisauce与Hooks
在components/SauceExample.js ,写下以下代码:
import { FlatList, Box, NativeBaseProvider, Center } from "native-base";
import React from "react";
import { useEffect } from "react";
import { create } from "apisauce";
import { useState } from "react";
export default function SauceExample() {
const [data, setData] = useState([]);
const api = create({
baseURL: "https://api.sampleapis.com/coffee",
});
const fetchData = () => {
//make request to baseURL + 'hot'
api
.get("/hot")
.then((response) => response.data)
.then((data) => setData(data));
};
const renderItem = ({ item }) => {
return (
<Box px={5} py={2} rounded="md" bg="primary.300" my={2}>
{item.title}
</Box>
);
};
useEffect(() => {
fetchData();
}, []);
return (
<NativeBaseProvider>
<Center flex={1}>
<Box> Using Apisauce </Box>
{data && (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
)}
</Center>
</NativeBaseProvider>
);
}
这将是输出:
如果你想从iced 路径中获得数据,你只需要在你的fetchData 函数中改变一行。
const fetchData = () => {
//make request to baseURL + 'iced'
api
.get("/iced")
.then((response) => response.data)
.then((data) => setData(data));
};
在Apisauce中使用async/await
想在你的代码中使用async 和await ?没问题。你可以像这样写你的代码。
const fetchData = async () => {
//make request to baseURL + 'iced'
const response = await api.get("/iced");
setData(response.data);
};
使用渲染道具来渲染数据
React中的props使我们的代码具有模块化和简洁性。例如,为了渲染数据,我们写了以下内容。
return (
<NativeBaseProvider>
<Center flex={1}>
{data && (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
)}
</Center>
</NativeBaseProvider>
);
为了缩短这个块,你可以使用渲染道具。
创建一个名为DataRenderer.js 的自定义组件。
import { FlatList, Box } from "native-base";
import React from "react";
export default function DataRenderer({ data }) {
const renderItem = ({ item }) => {
return (
<Box px={5} py={2} rounded="md" bg="primary.300" my={2}>
{item.title}
</Box>
);
};
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
/>
);
}
在你的其他文件中像这样使用它:
return (
<NativeBaseProvider>
<Center flex={1}>{data && <DataRenderer data={data} />}</Center>
</NativeBaseProvider>
);
我们的代码看起来更干净了!输出应该和以前一样:
用GraphQL和Apollo客户端获取数据
为什么使用GraphQL?
GraphQL是一种与REST完全不同的技术,同时仍然保持着易用性。
例如,如果使用REST向我们的Coffee API发出标准请求,你会做以下工作。
const response = await fetch(https://api.sampleapis.com/coffee/hot)
//further code ...
这将给出以下响应。
尽管这很好,但有一个小缺陷。我们的应用程序只需要title 字段。其余的数据对我们来说是不必要的。
这就是GraphQL的作用。为了只检索我们项目的title 和data 字段,我们将执行以下查询。
query HotCoffees{
allHots {
title
id
}
}
这将是服务器的响应。
总而言之,GraphQL只给你提供你需要的数据。
GraphQL示例用法
在你的components 文件夹中,创建一个名为CoffeeGraphQL.js 的文件。在这里,先写下下面这段代码。
import {
ApolloClient,
InMemoryCache,
ApolloProvider,
useQuery,
gql,
} from "@apollo/client";
import { Box, Center, NativeBaseProvider } from "native-base";
import React from "react";
import DataRenderer from "./DataRenderer";
//connect to GraphQL server:
const client = new ApolloClient({
uri: "https://api.sampleapis.com/coffee/graphql",
cache: new InMemoryCache(),
});
function RenderQuery() {
//define our query
const query = gql`
query HotCoffees {
allHots {
title
id
}
}
`;
//make a query
const { loading, error, data } = useQuery(query);
if (error) console.log(error);
return (
<NativeBaseProvider>
<Center flex={1}>
<Box> Using GraphQL </Box>
{loading && <Box>Loading data.. please wait</Box>}
{data && <DataRenderer data={data.allHots} />}
</Center>
</NativeBaseProvider>
);
}
从这段代码中可以得出一些推论:
client这个变量将我们的应用程序连接到GraphQL服务器上- 后来,我们做了一个查询,以获得
title和id字段 - 如果发生错误,将其记录在控制台中
- 当数据加载完毕后,将其显示在用户界面上
作为最后一步,我们现在必须将我们的RenderQuery 组件与我们的GraphQL客户端绑定。
要做到这一点,在CoffeeGraphQL.js 中添加以下代码:
export default function CoffeeGraphQL() {
return (
<ApolloProvider client={client}>
<RenderQuery />
</ApolloProvider>
);
}
这将是一个结果。
你的CoffeeGraphQL.js 文件应该看起来像这样。
import {
ApolloClient,
InMemoryCache,
ApolloProvider,
useQuery,
gql,
} from "@apollo/client";
import { Box, Center, NativeBaseProvider } from "native-base";
import React from "react";
import DataRenderer from "./DataRenderer";
//connect to GraphQL server:
const client = new ApolloClient({
uri: "https://api.sampleapis.com/coffee/graphql",
cache: new InMemoryCache(),
});CoffeeClass
function RenderQuery() {
const query = gql`
query HotCoffees {
allHots {
title
id
}
}
`;
//make a query
const { loading, error, data } = useQuery(query);
if (error) console.log(error);
return (
<NativeBaseProvider>
<Center flex={1}>
<Box>Using GraphQL</Box>
{loading && <Box>Loading data.. please wait</Box>}
{data && <DataRenderer data={data.allHots} />}
</Center>
</NativeBaseProvider>
);
}
export default function CoffeeGraphQL() {
return (
<ApolloProvider client={client}>
<RenderQuery />
</ApolloProvider>
);
}
用类组件获取数据
虽然现代React更倾向于使用功能组件,但使用类组件来构建你的应用程序的选项仍然存在。这对于维护遗留的React Native代码很有用。
创建一个名为CoffeeClass.js 的类,并编写以下代码块。
import { Center, NativeBaseProvider, Box } from "native-base";
import React, { Component } from "react";
import DataRenderer from "./DataRenderer";
export default class CoffeeClass extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentDidMount() {
this.fetchUsersAsync();
}
fetchUsersAsync() {
const URL = "https://api.sampleapis.com/coffee/hot";
fetch(URL)
.then((response) => response.json())
.then((list) => this.setState({ data: list }));
}
render() {
return (
<NativeBaseProvider>
<Center flex={1}>
<Box>Using class component</Box>
{this.state.data && <DataRenderer data={this.state.data} />}
</Center>
</NativeBaseProvider>
);
}
}
在这段代码中,我们告诉React在第一次渲染时执行fetchCoffee (componentDidMount)。这将从API获取数据并将其响应存储到data 状态变量中。最后,我们渲染了data 数组。
运行该代码。这将是输出结果:
有些人可能认为在componentWillMount 函数中获取数据更好,因为该函数是在组件安装前执行的。有两个理由证明你不应该这样做。
- 从React v17开始,它已被废弃。
- 当你在
componentWillMount()中使用Fetch API时,React Native会渲染你的数据而不等待第一次渲染。这将导致第一次出现空白屏幕。由于这个原因,节省的时间不会太多。
总结
在这篇文章中,我们探讨了一些在React Native中获取数据的常见策略。此时此刻,我选择在我的项目中使用Apisauce和Hooks。它们不仅使用起来非常简单,而且还很强大。因此,这带来了应用程序的安全性和效率。
非常感谢您的阅读!编码愉快!