用React Native获取数据的教程

607 阅读9分钟

React Native对于希望轻松构建移动应用的开发者来说是一个了不起的库。它提供了一种向前端显示信息的有效方式。但我们如何获得数据,以便我们的组件能够渲染它?

在这篇文章中,你将学习如何从API中获取数据并将其显示给用户。我们将用全面的代码样本介绍几种方法,以帮助你确定适合你的应用程序的最佳方法。

我们将介绍在React Native中获取数据的下列选项:

  • 使用内置的Fetch API
  • 使用Apisauce获取数据
  • 使用渲染道具来渲染数据
  • 使用GraphQL和Apollo客户端获取数据
  • 用类组件获取数据

为了展示React Native中的数据采购,我们将构建一个基本的应用程序,从Coffee API中获取一个项目列表。此外,我们将使用NativeBase UI库来向客户端渲染我们的数据。

最后,你的示例应用程序将看起来像这样。

Data Fetching in React Native Final Product

你可以从这个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,叫做dataloadingdata 钩子将保存取来的数据,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 />;
}

这将是输出结果:

Fetch API Fetching on Mount

在下一节,你将学习如何在用户点击按钮时渲染数据。
最后,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行,我们使用了条件性渲染。这将在点击时隐藏该组件。

Fetch API Fetching on Button Click

在下一节中,你将学习如何以固定的时间间隔获取数据。

我们的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 方法。后来,我们指定,如果这个组件从树上被删除,那么就清除这个间隔。这将防止内存泄漏。

这就是全部的内容了!你的代码运行起来应该没有任何问题。

Fetching Data in Intervals

你的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的一个封装器,甚至可以让你进行POSTPUTDELETE 的请求。

要安装该包,请运行这个终端命令:

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>
  );
}

这将是输出:

Using Apisauce with Hooks

如果你想从iced 路径中获得数据,你只需要在你的fetchData 函数中改变一行。

const fetchData = () => {
  //make request to baseURL + 'iced'
  api
    .get("/iced")
    .then((response) => response.data)
    .then((data) => setData(data));
};

Using Apisauce Iced

在Apisauce中使用async/await

想在你的代码中使用asyncawait ?没问题。你可以像这样写你的代码。

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>
  );

我们的代码看起来更干净了!输出应该和以前一样:

Rendering Data with Props

用GraphQL和Apollo客户端获取数据

为什么使用GraphQL?

GraphQL是一种与REST完全不同的技术,同时仍然保持着易用性。

例如,如果使用REST向我们的Coffee API发出标准请求,你会做以下工作。

const response = await fetch(https://api.sampleapis.com/coffee/hot)
//further code ...

这将给出以下响应。

Coffee API Using REST

尽管这很好,但有一个小缺陷。我们的应用程序只需要title 字段。其余的数据对我们来说是不必要的。

这就是GraphQL的作用。为了只检索我们项目的titledata 字段,我们将执行以下查询。

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服务器上
  • 后来,我们做了一个查询,以获得titleid 字段
  • 如果发生错误,将其记录在控制台中
  • 当数据加载完毕后,将其显示在用户界面上

作为最后一步,我们现在必须将我们的RenderQuery 组件与我们的GraphQL客户端绑定。

要做到这一点,在CoffeeGraphQL.js 中添加以下代码:

export default function CoffeeGraphQL() {
  return (
    <ApolloProvider client={client}>
      <RenderQuery />
    </ApolloProvider>
  );
}

这将是一个结果。

GraphQL Coffee API

你的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 数组。

运行该代码。这将是输出结果:

Class Component Coffee API

有些人可能认为在componentWillMount 函数中获取数据更好,因为该函数是在组件安装前执行的。有两个理由证明你不应该这样做。

  • 从React v17开始,它已被废弃
  • 当你在componentWillMount() 中使用Fetch API时,React Native会渲染你的数据而不等待第一次渲染。这将导致第一次出现空白屏幕。由于这个原因,节省的时间不会太多。

总结

在这篇文章中,我们探讨了一些在React Native中获取数据的常见策略。此时此刻,我选择在我的项目中使用Apisauce和Hooks。它们不仅使用起来非常简单,而且还很强大。因此,这带来了应用程序的安全性和效率。

非常感谢您的阅读!编码愉快!