FastAPI+React全栈开发20 使用useEffect与api通信

Chapter04 Setting Up a React Workflow

20 Communicate with APIs using useEffect

FastAPI+React全栈开发20 使用useEffect与api通信

We have already seen how React and its components transform the state and the data into a user interface. We will do all the funstuff of connecting our MongoDB layer to FastAPI and then powering a React frontend in the following chapter. Here, we will use a free mock rest API. However, we do need to address the problem of accessing external data and the management of external events in general. "External" with regards to what, you might wonder?

我们已经看到React及其组件是如何将状态和数据转换为用户界面的。在接下来的章节中,我们将做所有有趣的事情,将我们的MongoDB层连接到FastAPI,然后为React前端提供动力。在这里,我们将使用一个免费的mock rest API。然而,我们确实需要解决访问外部数据和管理外部事件的问题。你可能想知道,"外部"指的是什么?

Well, we have seen that React and its mighty hooks are centered around the task of synchronizing the UI to the state and the data. Components can contain other components, and together, they form what is known as a component tree, which is then constantly compared to the current state. React does all of this coordination work, what should be rendered, updated, and more.

好吧,我们已经看到React和它强大的钩子都是围绕着同步UI到状态和数据的任务。组件可以包含其他组件,它们一起构成了所谓的组件树,然后不断地将其与当前状态进行比较。React完成所有这些协调工作,包括应该呈现、更新的内容等等。

Events that are outside the flow of the React data flow process are called side effects. Some side effects might be setting or getting data values in local storage or session storage (maybe we want to save the data of logged-in users' until the next session), measuring the dimensions of some DOM element (for instance, we want to display different layouts for different browser sizes), and most notably, getting or fetching data from an external system, maybe in the form of a REST API call.

在React数据流流程之外的事件称为副作用。一些副作用可能是在本地存储或会话存储中设置或获取数据值(可能我们想要保存登录用户的数据直到下一个会话),测量某些DOM元素的尺寸(例如,我们想要为不同的浏览器大小显示不同的布局),最明显的是从外部系统获取或获取数据,可能以REST API调用的形式。

When working with React, one thing to always bear in mind is that it works in a continuous data flow, with an underlying system constantly scanning for updates and ready to re-render components that it deems in need of an update. We will illustrate this with a simple example. We are working on our Cars Sales application, and we need to list all the users that were kind (and smart enough!) and registered an account.

在使用React时,要始终记住的一件事是,它工作在一个连续的数据流中,底层系统不断地扫描更新,并准备重新呈现它认为需要更新的组件。我们将用一个简单的例子来说明这一点。我们正在开发Cars Sales应用程序,我们需要列出所有善良(并且足够聪明!)并注册了帐户的用户。

The task at hand is a simple and common one. We have a dedicated page, it will probably live in a URL called /users or something similar, and it should be populated with the data (think of a JavaScript array of objects) from an external API. This API will be powered by FastAPI, but for now, we will use a readymade mock solution called Regres.

手头的任务简单而普通。我们有一个专用的页面,它可能位于一个名为/users或类似的URL中,并且应该使用来自外部API的数据(想象一下JavaScript对象数组)填充它。这个API将由FastAPI提供支持,但是现在,我们将使用一个称为gres的现成模拟解决方案。

The GET all we need to make should be directed toward the URL, https://reqres.in/api/users.

我们需要进行的GET操作应该指向URL https://reqres.in/api/users。

We already understand how to make components, provide them props, and set their state, so that shouldn't be a problem. But what are we going to do about loading data from an external API? we'll just use something such as Fetch or Axios, right? Like we were using a normal plain JavaScript app.

我们已经了解了如何制作组件,为它们提供道具,并设置它们的状态,所以这应该不是问题。但是如何从外部API加载数据呢?我们会使用Fetch或Axios之类的东西?就像我们在使用普通的JavaScript应用程序一样。

Let's give it a try: Modify the App.js in a way that includes a standard fetch all to the API, including the json method to get the data to console.

让我们尝试一下:修改App.js,使其包含一个标准的抓取API,包括将数据获取到控制台的json方法。

jsx 复制代码
import Header from "./components/Header";

function App() {
    fetch("https://reqres.in/api/users")
        .then(resp => resp.json())
        .then(json => console.log(json))
    return (
        <div className="App max-w-3xl mx-auto h-full">
            <Header/>
        </div>
    );
}

export default App;

Our app will continue to be blank with an orange header, but if you take a look at the console in Chrome, you will see that we are actually getting our users back, six of them. So, we just need to put them in a state variable, and we should be good to go, right?

我们的应用仍然是空白的,带有一个橙色的标题,但如果你在Chrome中查看控制台,你会发现我们实际上找回了6个用户。所以,我们只需要把它们放到一个状态函数中,就可以了,对吧?

Let's add a state variable and a corresponding set method by using our old friend, the useState hook (warning: the following code is wrong, and it will bomb your brwser and, poteentially, get you into trouble with your API provider!).

让我们通过使用我们的老朋友useState钩子来添加一个状态变量和一个相应的set方法(警告:下面的代码是错误的,它会使您的浏览器崩溃,并且可能会使您与API提供程序陷入麻烦!)

jsx 复制代码
import Header from "./components/Header";
import {useState} from "react";

function App() {
    let [users, setUsers] = useState([])
    fetch("https://reqres.in/api/users")
        .then(resp => resp.json())
        .then(json => setUsers(json["data"]))
    return (
        <div className="App max-w-3xl mx-auto h-full">
            <Header/>
            <ul>
                {users.map(user => {
                    return (
                        <li key={user.id}>{user.email}</li>
                    )
                })}
            </ul>
        </div>
    );
}

export default App;

The preceding code just won't run, or it will run but not in the way that you might expect. If you manage to pen the developer's console in Chrome, you will see that the page is constantly making requests to our pooer API server, flooding it in the process. Why is that?

前面的代码不会运行,或者它会运行,但不是以您期望的方式运行。如果你设法在Chrome中编写开发者控制台,你会看到页面不断向我们可怜的API服务器发出请求,在此过程中淹没它。为什么呢?

I found this problem very illustrative of the way React works, and once you get the hang of it, it will begin to make sense. The problem is located at the top of the App function. We declared a state variable with useState, and then we naively proceeded to set its value after a fetch call to our external API. After firing the setUsers method, React noticed that there is a change of a state variable, so a re-render is in order. Then, React just calls the function agin, re-encounters the fetch call, sets the value of a state variable, and... you get it. We are in an infinite loop.

我发现这个问题很好地说明了React的工作方式,一旦你掌握了它的窍门,它就会开始变得有意义。问题出在App功能的顶部。我们用useState声明了一个状态变量,然后在调用外部API后,天真地设置了它的值。在触发setUsers方法后,React注意到状态变量发生了变化,因此需要重新呈现。然后,React只是再次调用该函数,再次遇到fetch调用,设置状态变量的值,然后......你懂的。我们处在一个无限循环中。

The useEffect hook is meant for cases like this, interacting with the world outside of our React app in a safe and controlled way.

useEffect钩子就是为这种情况设计的,它以一种安全和可控的方式与React应用程序之外的世界进行交互。

The syntax of the hook is quite simple, import it directly from React, and then call it inside our component with the following form.

钩子的语法非常简单,直接从React中导入它,然后在我们的组件中用下面的形式调用它。

jsx 复制代码
useEffect(() => {
    console.log("This just happened!")
}, []);

Note that useEffect takes in two arguments, the first is a function that will execute aftrer every render, and the second one is an array of dependencies in which we list all the values that we want to monitor for a channge. If any of these values change, the function declared as the first argument will run (again).

请注意,useEffect接受两个参数,第一个是每次呈现后执行的函数,第二个是一个依赖项数组,其中列出了我们想要监视变化的所有值。如果这些值中的任何一个发生变化,作为第一个参数声明的函数将(再次)运行。

Armed with this knowledge, we can rurn to out App component and try something like this.

有了这些知识,我们可以运行我们的App组件并尝试这样做。

jsx 复制代码
import Header from "./components/Header";
import {useEffect, useState} from "react";

function App() {
    let [users, setUsers] = useState([])
    useEffect(() => {
        fetch("https://reqres.in/api/users")
            .then(resp => resp.json())
            .then(json => setUsers(json["data"]))
    }, []);
    return (
        <div className="App max-w-3xl mx-auto h-full">
            <Header/>
            <ul>
                {users.map(user => {
                    return (
                        <li key={user.id}>{user.email}</li>
                    )
                })}
            </ul>
        </div>
    );
}

export default App;

Like some kind of magic, the page seems to display our data just the way we wanted it, alist of user emails, for a total of six (our API will not give us more than that, but it is more than enough for our purposes). Add after this.

就像某种魔法一样,页面似乎以我们想要的方式显示我们的数据,用户电子邮件列表,总共六个(我们的API不会给我们更多,但对于我们的目的来说已经足够了)。在此之后添加。

You might be wondering why we passed an empty array to our useEffect function. Simply put, this array, also know as a dependency array, allows us to control when the useEffect will fire. In our case, we provided an empty dependency array so the useEffect hook function will fire off only once, after the initial render. If we provided a state variable, controlled via a useState hook to the array, useEffect would fire every time that provided state variable changed.

您可能想知道为什么要将一个空数组传递给useEffect函数。简单地说,这个数组(也称为依赖数组)允许我们控制何时触发useEffect。在我们的例子中,我们提供了一个空的依赖数组,因此useEffect钩子函数只会在初始渲染之后触发一次。如果我们提供了一个状态变量,通过useState钩子控制到数组,useEffect将在每次提供的状态变量改变时触发。

The React documentation on the subject is a very useful read: https://reactjs.org/docs/hooks-reference.html. Now, since we want to showcase our dependency array, we can make use of the fact that our REST API of choice offers us two pages of users: https://reqres.in/aip/users?page=2.

关于这个主题的React文档非常有用:https://reactjs.org/docs/hooks-reference.html。现在,由于我们想要展示我们的依赖项数组,我们可以利用我们所选择的REST API为我们提供了两个用户页面:https://reqres.in/aip/users?page=2。

The code for adding the toggle logic is straightforward.

添加切换逻辑的代码很简单。

jsx 复制代码
import Header from "./components/Header";
import {useEffect, useState} from "react";

function App() {
    let [users, setUsers] = useState([])
    let [page, setPage] = useState(1)

    useEffect(() => {
        fetch(`https://reqres.in/api/users?page=${page}`)
            .then(resp => resp.json())
            .then(json => setUsers(json["data"]))
    }, [page]);

    return (
        <div className="App max-w-3xl mx-auto h-full">
            <Header/>
            <button
                className="border border-gray-500 rounded-md p-2 m-5"
                onClick={() => {
                    page === 1 ? setPage(2) : setPage(1)
                }}
            >
                Toggle users
            </button>
            <ul>
                {users.map(user => {
                    return (
                        <li key={user.id}>{user.email}</li>
                    )
                })}
            </ul>
        </div>
    );
}

export default App;

We also made sure to short-circuit the users' array variable and the JSX mapping logic, and added the page variable to the useEffect function's dependency array, this way the function will execute every time there is a page number change, or toggle.

我们还确保缩短了用户的数组变量和JSX映射逻辑,并将页面变量添加到useEffect函数的依赖项数组中,这样每当有页码更改或切换时,该函数都会执行。

Note that in the book's repository this application is separated and called Users, in order to preserve all the code form both apps.

请注意,在本书的存储库中,这个应用程序是分开的,并称为Users,以便保留两个应用程序的所有代码。

Finally, useEffect only fires when the contents of the dependency array change, and in our case, that is the page variable. Now, clicking on the button fetches us the first or second page of the users and no API calls are made in between, except for the initial fetch that follows immediately after the first component render.

最后,useEffect仅在依赖项数组的内容发生变化时触发,在我们的示例中,这是页面变量。现在,点击按钮获取用户的第一页或第二页,中间没有API调用,除了在第一个组件呈现之后立即进行的初始获取。

Like with useState, there are much more subtleties involved. For example, we can provide a cleanup function at the bottom of the useEffect body to make sure that any long-lasting effects are removed and so on, but this should give you a basic idea of how to handle actions that reach out to an external API.

与useState一样,这里涉及到更多的微妙之处。例如,我们可以在useEffect主体的底部提供一个清理函数,以确保删除任何持久的效果等等,但这应该让您对如何处理接触外部API的操作有一个基本的了解。

There are several other hooks that you will want to use in your projects, the useMemo book for memoizing values of a function helps us to avoid unnecessary repeated costly function calls. Additionally, useContext allows React to cover an entire area of components and pass values directly without having to pass them through several components that might not actually need it (prop drilling). We can even create our own hooks and abstract functionality that can be reused in several places of the app, ensuring better maintainability and less repetition. In the upcoming chapters, we will use a couple of hooks in order to achieve the desired functionality elegantly and simply.

在你的项目中还有一些其他的钩子,用于记忆函数值的useMemo书可以帮助我们避免不必要的重复的昂贵的函数调用。此外,useContext允许React覆盖组件的整个区域并直接传递值,而不必通过几个实际上可能不需要它的组件传递它们(prop drilling)。我们甚至可以创建自己的钩子和抽象功能,这些功能可以在应用程序的多个地方重用,从而确保更好的可维护性和更少的重复。在接下来的章节中,我们将使用几个钩子来优雅而简单地实现所需的功能。

In conclusion, a personal note, I tried to learn React while there were no hooks around, and I must be honest, I did not like it. The state management with class components and life cycle methods just did not sit well with me, and I admit that, probably, my background and lack of classical Computer Science training played a role in that. With the entrance of hooks, the whole ecosystem just became much clearer and cleaner, and the mapping of business logic to UIs is much more streamlined and, well, logical. I can only suggest that you take some time and dive into the hooks system, it will be worth it, I promise!

总而言之,就个人而言,我试着在没有钩子的情况下学习React,我必须诚实地说,我不喜欢它。使用类组件和生命周期方法的状态管理并不适合我,我承认,这可能与我的背景和缺乏经典计算机科学培训有关。随着钩子的引入,整个生态系统变得更加清晰和干净,业务逻辑到ui的映射也更加精简和合乎逻辑。我只能建议你花点时间深入研究一下钩子系统,我保证这是值得的!

You now have the knowledge that is necessary to set and get states in your components or apps and to communicate with external API services in a predictable and controllable way, while crafting clean and simple code. Just using React and its hooks can give you web developer superpowers, but there is a whole world of packages and modules built around React that is just as important as the core libraries.

现在,您已经掌握了在组件或应用程序中设置和获取状态以及以可预测和可控的方式与外部API服务通信所必需的知识,同时编写干净简单的代码。仅仅使用React和它的钩子就可以给你的web开发人员带来超级能力,但是围绕React构建的整个世界的包和模块与核心库一样重要。

相关推荐
我要洋人死19 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人30 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人31 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR36 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香38 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969341 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
小牛itbull3 小时前
ReactPress:重塑内容管理的未来
react.js·github·reactpress