使用Axios和React Native来管理API请求

简介

在构建网络或移动应用程序时,你经常需要向API发出网络请求。你可以通过这些网络请求来验证用户,更新资源,或从你自己的服务器或第三方API中获取资源。如果你想在浏览器环境中进行API请求,Fetch API就会很方便。

React Native也有一个类似于浏览器的内置Fetch API,专门用于从你的移动应用程序与API联网。然而,有一些替代的库,如Axios,你可以使用,而不是依赖本地的Fetch API。

如果你只想从服务器上检索资源,内置的Fetch API可能就足够了。对于更复杂的网络需求来说,Axios可能是一个更好的选择,因为它带有额外的功能,比如拦截网络请求和响应。

此外,除其他原因外,大多数开发人员喜欢Axios而不是内置的fetch API,因为它具有同构性和开箱即用的JSON转换。

在这篇文章中,你将学习如何在React Native应用中使用Axios管理API请求。

Axios简介

Axios是一个流行的、同构的HTTP客户端。这意味着它可以在浏览器和Node运行时环境中运行。因此,你可以在Node、浏览器和React Native中使用相同的代码库来进行API请求。

Axios有几个特点,如支持Promise API,自动JSON转换,以及拦截网络请求和响应等等。

使用Expo CLI设置一个简单的React Native应用

在本教程中,我们将使用一个简单的React Native应用程序,使用Expo的托管工作流来设置。

如果你已经设置了一个React Native应用程序,你可以继续到下一节。否则,在React Native文档中就有关于Expo管理的工作流的设置说明,可以让你在几分钟内启动并运行。

Axios简介

Axios是最容易学习和使用的HTTP客户端之一。提出API请求就像向Axios传递配置对象或用必要的参数调用适当的方法一样简单。你将在本节中学习Axios的基础知识。

以下各小节中强调的功能是你在使用Axios时最常用的功能。

如何安装Axios

根据你使用的软件包管理器,在终端窗口输入下面的一个命令,然后点击返回键来安装Axios。

// axios
npm install axios
// yarn
yarn add axios

如何使用Axios向API发出请求

当使用Axios调用API时,你可以向Axios传递一个配置对象,或者为你想要执行的相应CRUD操作调用一个方法。

例如,你可以通过以下两种方式之一向/api/users 端点发出GET请求。

import axios from 'axios';
const baseUrl = 'https://reqres.in';
// Passing configuration object to axios
axios({
  method: 'get',
  url: `${baseUrl}/api/users/1`,
}).then((response) => {
  console.log(response.data);
});
// Invoking get method to perform a GET request
axios.get(`${baseUrl}/api/users/1`).then((response) => {
  console.log(response.data);
});

你也可以使用async/await,而不是像上面的例子中那样使用Promise链。

还有其他几个字段,如baseURL,transformRequest,transformResponse, 和headers, 等等,你可以在传递给Axios的配置对象中包含这些字段。

import axios from 'axios';
const baseUrl = 'https://reqres.in';
// Passing configuration object to axios
const fetchUser = async () => {
  const configurationObject = {
    method: 'get',
    url: `${baseUrl}/api/users/1`,
  };
  const response = await axios(configurationObject);
  console.log(response.data);
};
// Invoking get method to perform a GET request
const fetchUser = async () => {
  const url = `${baseUrl}/api/users/1`;
  const response = await axios.get(url);
  console.log(response.data);
};

与内置的Fetch API不同,Axios将为你把响应转换为JSON,而不是开箱即用。

如何使用Axios进行多个并发的API请求

你可以使用Axios的Promise API的Promise.allPromise.allSettled 方法,从React Native应用中提出多个并发的API请求。

在下面的代码片段中,所有的API请求都会成功;将传递给axios.get 方法的URI改为不存在的URI,看看如果有些请求不成功会怎样。

const concurrentRequests = [
      axios.get(`${baseUrl}/api/users/1`),
      axios.get(`${baseUrl}/api/users/2`),
      axios.get(`${baseUrl}/api/users/3`),
    ];
   // Using Promise.all
    Promise.all(concurrentRequests)
      .then((result) => {
        console.log(result);
      })
      .catch((err) => {
        console.log(err);
      });
    // Using Promise.allSettled
    Promise.allSettled(concurrentRequests)
      .then((result) => {
        console.log(result);
      })
      .catch((err) => {
        console.log(err);
      });

请注意,如果输入的Promise之一被拒绝,Promise.all 方法会立即拒绝。如果你想看到所有的或没有的API请求成功,请使用Promise.all

另一方面,Promise.allSettled ,等待所有的输入许诺被拒绝或满足。然后,你可以检查每个响应对象的fulfilledrejected 状态。

如何在Axios中中止网络请求

Axios提供了中止网络请求的功能。这个功能在React Native中的一个典型用例是,当一个组件被卸载而数据仍在飞行时,在useEffect 钩中取消网络请求。

你可以阅读下面的代码片段来了解如何使用这个功能。

useEffect(() => {
    const source = axios.CancelToken.source();
    const url = `${baseUrl}/api/users/${userId}`;
    const fetchUsers = async () => {
      try {
        const response = await axios.get(url, { cancelToken: source.token });
        console.log(response.data);
      } catch (error) {
        if(axios.isCancel(error)){
          console.log('Data fetching cancelled');
        }else{
         // Handle error
        }
      }
    };
    fetchUsers();
    return () => source.cancel("Data fetching cancelled");
  }, [userId]);

如何创建Axios的实例

你也可以用自定义配置创建一个Axios的实例。然后,使用该实例所暴露的方法来进行网络请求。

Axios会将创建实例时传递的配置对象与传递给实例方法的配置合并。

const axiosInstance = axios.create({ baseURL: 'https://reqres.in/' });
axiosInstance.get('api/users/1').then((response) => {
  console.log(response.data);
});

使用Axios和React Native来管理API请求

在本节中,你将学习如何在React Native应用程序中使用Axios管理API请求。你将使用Axios来执行一个简单的CRUD(创建、读取、更新和删除)操作。

如何在React Native中管理API密钥

大多数第三方API需要秘密证书才能访问。在客户端的源代码中保留你的秘密API密钥并不是一个好主意。如果你这样做,任何检查你捆绑的应用程序的代码的人都可以访问你的私人密钥。

管理你的API密钥的推荐方法之一是在第三方API和你的应用程序之间创建一个协调层。例如,你可以使用一个无服务器函数来安全地访问你的API密钥。

你的应用程序将调用无服务器函数所暴露的端点。无服务器函数将安全地访问你的API密钥,调用第三方API,检索你需要的资源,并将其转发给你的移动应用。

管理网络请求-响应周期中的应用状态

当你向一个API发起网络请求时,请求要么成功,要么失败。因此,从请求到收到服务器的响应,跟踪你的应用程序的不同状态很重要。

当数据仍在飞行时,你可以显示一个加载指标。如果CRUD操作成功了,你就向用户显示一个 “成功 “的消息。如果它失败了,你会显示一个适当的错误信息。

这很重要,因为使用你的应用程序的客户可能有一个缓慢的互联网连接或没有互联网接入。API服务器有时可能会出现停机。跟踪你的应用程序的状态并显示适当的信息将提供良好的用户体验。

我们将在本文中使用reqresREST API。它是一个由假数据组成的占位符API。你可以使用API测试工具,如Postman或Insomnia,来玩弄这些端点。

如何在React Native中使用Axios进行GET请求

在这一节中,我们将向/api/users 端点发出GET请求,以检索一个用户。如果你想从服务器上请求一个资源,GET是你使用的HTTP方法。

如下面的代码片段所示,我们在状态中存储了用户ID。你可以在连接到Load User 按钮onPress 事件处理程序中改变用户ID。改变用户ID将触发一个GET请求到useEffect 钩子里面的API。

触发网络请求后,我们在屏幕上显示一个加载指标。如果我们成功地获取了数据,我们就会更新状态并移除加载指示器。如果我们由于某种原因无法获取数据,我们会停止加载指示器并显示一个适当的错误信息。

如果用户在从服务器获得响应之前决定关闭应用程序,我们在清理函数中中止网络请求。在useEffect 钩子中检查效果函数的返回值。

下面是在App.js 组件中的代码的样子。

import axios from "axios";
import React, { useState, useEffect } from "react";
import {
  StyleSheet,
  Text,
  ScrollView,
  View,
  Button,
  Image,
  Platform,
} from "react-native";
import Constants from "expo-constants";
const baseUrl = "https://reqres.in";
function User({ userObject }) {
  return (
    <View>
      <Image
        source={{ uri: userObject.avatar }}
        style={{ width: 128, height: 128, borderRadius: 64 }}
      />
      <Text style={{ textAlign: "center", color: "white" }}>
        {`${userObject.first_name} ${userObject.last_name}`}
      </Text>
    </View>
  );
}
export default function App() {
  const [userId, setUserId] = useState(1);
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [hasError, setErrorFlag] = useState(false);
  const changeUserIdHandler = () => {
    setUserId((userId) => (userId === 3 ? 1 : userId + 1));
  };
  useEffect(() => {
    const source = axios.CancelToken.source();
    const url = `${baseUrl}/api/users/${userId}`;
    const fetchUsers = async () => {
      try {
        setIsLoading(true);
        const response = await axios.get(url, { cancelToken: source.token });
        if (response.status === 200) {
          setUser(response.data.data);
          setIsLoading(false);
          return;
        } else {
          throw new Error("Failed to fetch users");
        }
      } catch (error) {
        if(axios.isCancel(error)){
          console.log('Data fetching cancelled');
        }else{
          setErrorFlag(true);
          setIsLoading(false);
        }
      }
    };
    fetchUsers();
    return () => source.cancel("Data fetching cancelled");
  }, [userId]);
  return (
    <ScrollView contentContainerStyle={styles.container}>
      <View style={styles.wrapperStyle}>
        {!isLoading && !hasError && user && <User userObject={user} />}
      </View>
      <View style={styles.wrapperStyle}>
        {isLoading && <Text> Loading </Text>}
        {!isLoading && hasError && <Text> An error has occurred </Text>}
      </View>
      <View>
        <Button
          title="Load user"
          onPress={changeUserIdHandler}
          disabled={isLoading}
          style={styles.buttonStyles}
        />
      </View>
    </ScrollView>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "dodgerblue",
    alignItems: "center",
    justifyContent: "center",
    marginTop: Platform.OS === "ios" ? 0 : Constants.statusBarHeight,
  },
  wrapperStyle: {
    minHeight: 128,
  },
  buttonStyles: {
    padding: 100,
  },
});

在上述组件中,我们使用useEffect 钩子来执行副作用,如从API中获取数据。然而,这在React的未来版本中可能会发生变化。你可以查看React文档,了解更多关于React Suspense的信息;这是一个数据获取功能,将很快在React的稳定版本中出现。

如何在React Native中使用Axios进行POST请求

在本节中,你将学习如何发出POST请求。POST是一种HTTP方法,用于向服务器发送数据以更新或创建资源。

我们使用的占位符API为创建资源暴露了/api/users 端点。成功创建资源后,你会得到一个201状态代码的响应。

在Axios中发出POST请求与发出GET请求类似。大多数情况下,POST请求是用用户生成的数据通过表单提交的。提交的数据可以是来自客户的登录、注册或反馈表。这种数据在提交之前需要在客户端进行验证。

在建立一个复杂的应用程序时,你可以使用其中一个表单包来进行数据验证。大多数的包都有很好的架构和优化,并且在它们背后有一个很好的社区。然而,在将一个库集成到你的应用程序中之前,要探索其中的利弊。特别是你添加到你的应用程序中的额外的包的大小,以及它可能引入的潜在安全漏洞。

有两个主要的React包用于管理表单。这些包是Formik和React Hook Form。如果你有兴趣,你可以找到很多关于React中表单验证的文章。

我们在下面的代码片断中为用户的全名和电子邮件设置了一个React Native表单。TextInput ,这两个组件都是受控组件。理想情况下,当用户填写表单时,你会实时地进行数据验证。然而,这里不是这样的,因为表单数据验证不在本文的范围内。

在点击提交按钮后,TextInput 字段和提交按钮被禁用,然后再显示一条信息以显示你正在创建资源。禁用提交按钮可以确保用户不会进行多次提交。

在成功提交POST请求后,你会向用户显示一条成功信息。

import axios from "axios";
import React, { useState } from "react";
import {
  StyleSheet,
  Text,
  ScrollView,
  View,
  Button,
  Platform,
  TextInput,
} from "react-native";
import Constants from "expo-constants";
const baseUrl = "https://reqres.in";
export default function App() {
  const [fullName, setFullName] = useState("");
  const [email, setEmail] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const onChangeNameHandler = (fullName) => {
    setFullName(fullName);
  };
  const onChangeEmailHandler = (email) => {
    setEmail(email);
  };
  const onSubmitFormHandler = async (event) => {
    if (!fullName.trim() || !email.trim()) {
      alert("Name or Email is invalid");
      return;
    }
    setIsLoading(true);
    try {
      const response = await axios.post(`${baseUrl}/api/users`, {
        fullName,
        email,
      });
      if (response.status === 201) {
        alert(` You have created: ${JSON.stringify(response.data)}`);
        setIsLoading(false);
        setFullName('');
        setEmail('');
      } else {
        throw new Error("An error has occurred");
      }
    } catch (error) {
      alert("An error has occurred");
      setIsLoading(false);
    }
  };
  return (
    <ScrollView contentContainerStyle={styles.container}>
      <View>
        <View style={styles.wrapper}>
          {isLoading ? (
            <Text style={styles.formHeading}> Creating resource </Text>
          ) : (
            <Text style={styles.formHeading}>Create new user</Text>
          )}
        </View>
        <View style={styles.wrapper}>
          <TextInput
            placeholder="Full Name"
            placeholderTextColor="#ffffff"
            style={styles.input}
            value={fullName}
            editable={!isLoading}
            onChangeText={onChangeNameHandler}
          />
        </View>
        <View style={styles.wrapper}>
          <TextInput
            placeholder="Email"
            placeholderTextColor="#ffffff"
            style={styles.input}
            value={email}
            editable={!isLoading}
            onChangeText={onChangeEmailHandler}
          />
        </View>
        <View>
          <Button
            title="Submit"
            onPress={onSubmitFormHandler}
            style={styles.submitButton}
            disabled={isLoading}
          />
        </View>
      </View>
    </ScrollView>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#252526",
    alignItems: "center",
    justifyContent: "center",
    marginTop: Platform.OS === "ios" ? 0 : Constants.statusBarHeight,
  },
  formHeading: {
    color: "#ffffff",
  },
  wrapper: {
    marginBottom: 10,
  },
  input: {
    borderWidth: 2,
    borderColor: "grey",
    minWidth: 200,
    textAlignVertical: "center",
    paddingLeft: 10,
    borderRadius: 20,
    color: "#ffffff",
  },
  submitButton: {
    backgroundColor: "gray",
    padding: 100,
  },
});

如何在React Native中使用Axios进行PUT请求

更新一个资源需要使用PUT或PATCH方法,不过我将重点介绍PUT。

如果一个资源存在,使用PUT方法会完全覆盖它,如果不存在则会创建一个新资源。另一方面,如果资源存在,PATCH会对其进行部分更新,如果不存在则不做任何事情。

向一个API发出PUT请求与发出POST请求类似。唯一的区别是你传递给Axios的配置对象,或者你需要调用HTTP方法来向API发出PUT请求。

你可以用下面的代码替换POST请求中的onSubmitFormHandler ,以进行PUT请求。为了完整起见,我在下面的事件处理程序中使用了Promise链而不是async/await。

 const onSubmitFormHandler = (event) => {
    if (!fullName.trim() || !email.trim()) {
      alert("Name or Email is invalid");
      return;
    }
    setIsLoading(true);
    const configurationObject = {
      url: `${baseUrl}/api/users/2`,
      method: "PUT",
      data: { fullName, email },
    };
    axios(configurationObject)
      .then((response) => {
        if (response.status === 200) {
          alert(` You have updated: ${JSON.stringify(response.data)}`);
          setIsLoading(false);
          setFullName("");
          setEmail("");
        } else {
          throw new Error("An error has occurred");
        }
      })
      .catch((error) => {
        alert("An error has occurred");
        setIsLoading(false);
      });
  };

如何在React Native中使用Axios做一个DELETE请求

你可以用Axios做DELETE请求,就像你做POST和PUT请求一样。

就像它的名字一样,DELETE请求将从服务器端删除一个资源。你可以用下面的事件处理程序替换POST请求的代码onSubmitFormHandler ,以实现DELETE请求。代码的其余部分保持不变。

const onSubmitFormHandler = async (event) => {
    if (!fullName.trim() || !email.trim()) {
      alert("Name or Email is invalid");
      return;
    }
    setIsLoading(true);
    try {
      const response = await axios.delete(`${baseUrl}/api/users/2`, {
        fullName,
        email,
      });
      if (response.status === 204) {
        alert(` You have deleted: ${JSON.stringify(response.data)}`);
        setIsLoading(false);
        setFullName('');
        setEmail('');
      } else {
        throw new Error("Failed to delete resource");
      }
    } catch (error) {
      alert("Failed to delete resource");
      setIsLoading(false);
    }
  }; 

结论

当你建立一个移动应用程序时,向API发出网络请求是不可避免的,而Axios是目前最流行的HTTP客户端之一。它带有附加功能,使网络尽可能简单。

你的应用程序与之互动的API可以是自我托管的或第三方的API。为了改善用户体验,对网络请求-响应周期的有效管理是最重要的。

反过来说,你需要权衡在你的移动应用中添加第三方软件包(如Axios)的利弊。尽管Axios是一个流行的、维护良好的软件包,但根据Packagephobia的数据,它将使你的包的大小增加367kB。

尽管在使用强大的移动设备时,这个包的大小可能看起来只是对你的应用程序的一个小的补充,但你需要考虑到它对使用不那么强大的移动设备的用户的影响。同样地,你需要确保像Axios这样的第三方软件包不会给你的应用程序带来安全漏洞。

The postUsing Axios with React Native to manage API requestsappeared first onLogRocket Blog.

评论

发表回复