reactjs调用带参方法_在reactjs中进行api调用的模式

reactjs调用带参方法

There are a lot of approaches we can make to do an API call in React. I will share what I’ve learned and discovered after dealing with different types of making an API call in React. This includes executions through hooks and without hooks.

我们可以采用许多方法在React中进行API调用。 我将分享在React中处理不同类型的API调用后所学到的知识和发现。 这包括通过钩子执行和不通过钩子执行。

I’m also here to try and rethink the approaches that we always have but we tend to forget because of the new things. How well do we know the fundamentals?

我也在这里尝试重新思考我们一直使用的方法,但是由于新事物,我们往往会忘记。 我们对基本原理的了解程度如何?

Without further ado, let’s get started!

事不宜迟,让我们开始吧!

ing设计我们的挂钩 (👨‍💻 Designing our Hooks)

Before we get started with the different patterns, let’s start designing how our hooks will look like and also discuss why it was structured that way. I will start by creating a specialized API hook to fetch a list of todos.

在开始使用不同的模式之前,让我们开始设计钩子的外观,并讨论其构造方式。 我将从创建专门的API挂钩开始以获取待办事项列表。

import {
  useState,
  useCallback,
} from 'react';


export const getTodos = async (options) => {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos', options);
  const data = await response.json();
  
  // add transformers here if needed
  
  return data;
};


export const useGetTodos = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);
  
  const execute = async (options = {}) => {
    try {
      setIsLoading(true);
      const todos = await getTodos(options);
      setData(todos);
      return todos;
    } catch (e) {
      setError(e);
      setIsLoading(false);
      throw e;
    }
  };
  
  return {
    isLoading,
    error,
    data,
    execute: useCallback(execute, []), // to avoid infinite calls when inside a `useEffect`
  };
};

We structured our hook this way in order to be flexible in different scenarios. The different scenarios will be discussed as we go through the list. I want to say that there are still unknowns and this isn’t perfect, but this is progress.

我们以这种方式构造挂钩,以便在不同情况下保持灵活性。 我们将在清单中讨论不同的情况。 我想说的是仍然存在未知数,虽然这还不是完美的,但这是进步。

I know you will be having a lot of questions, here are some I can answer right now on this section, and let’s see if you can get an answer as we go through. If you don’t get an answer, let’s discuss it in another section.

我知道您会遇到很多问题,以下是我现在可以在本节中回答的一些问题,让我们看看您能否在我们逐步解决的过程中得到答案。 如果您没有得到答案,让我们在另一部分中讨论它。

  • Why do we have two functions re-exported? One is the getTodos and another one is the useGetTodos

    为什么我们要重新导出两个功能? 一个是getTodos ,另一个是useGetTodos

The reason we did that is to allow the consumer — the one who will be using our hook, to have the ability to execute the API in a “hook way” version or in a “non-hook way” version.

我们这样做的原因是允许消费者(将使用我们的钩子的用户)能够以“钩子”版本或“非钩子”版本执行API。

The idea here is that, to be able to reuse the same API core logic across the app. This is helpful when you are dealing with SSR with Next.js or Gatsby or also if you don’t need states into it.

这里的想法是,能够在应用程序中重用相同的API核心逻辑。 当您使用Next.js或Gatsby处理SSR或不需要状态时,这将很有帮助。

  • Should the logic be avoided in the hook implementation?

    挂钩实现中应避免逻辑吗?

As much as possible we should avoid some logic on the hook that may alter the behavior of the API call. Let’s say you add some data transformers. It’s better to add that in the non-hook version so that you have one place for it. In that way, we can add test coverage and all consumers regardless if using the hook and non-hook version will benefit from it.

我们应尽可能避免在钩子上使用某些可能会更改API调用行为的逻辑。 假设您添加了一些数据转换器。 最好将其添加到非挂钩版本中,以便您拥有一个位置。 这样,我们可以增加测试覆盖率,并且无论使用钩子版本还是非钩子版本,我们都可以增加所有使用者的利益。

  • What is the use of the return value from the hook if the execute method returns the data already?

    如果execute方法已经返回了数据,那么钩子的返回值有什么用

There are scenarios where you need the data immediately on the handler function (i.e onSubmit handler) for extra processing instead of relying on useEffect. Both are important, but as much as possible if we can do it on the handler function, do it there instead of listening through the useEffect for changes and react to it.

在某些情况下,您需要立即在处理程序函数(即onSubmit处理程序)上获取数据以进行额外处理,而不是依赖useEffect 。 两者都很重要,但是如果我们可以在处理程序函数上做到这一点,则应尽可能多地在那做,而不是通过useEffect进行更改并做出React。

If the process depends on each other like the 2nd API call needs the result of the 1st API call, it’s hard to “chain” those process together through useEffect.

如果进程彼此依赖,例如第二个API调用需要第一个API调用的结果,则很难通过useEffect这些进程“链接”在一起。

Those are the questions I can answer now. I hope some of it gives you a better idea of it. Let’s get started now for the different patterns we will be learning together!

这些是我现在可以回答的问题。 我希望其中一些可以使您对此有所了解。 现在就开始我们一起学习的不同模式吧!

Call组件安装上的API调用 (💡 API Call on Component Mount)

Scenario: When the user hits the page, we want to hit an API call on the component mount.

场景:当用户点击页面时,我们想点击组件安装上的API调用。

import React from 'react';


export const App = () => {
  const {
    isLoading,
    data,
    execute,
  } = useGetTodos();
  
  useEffect(() => {
    try {
      execute();  
    } catch() {}
  }, [
    execute,  
  ]);
  
  // render `data` below
  // use `isLoading` to show some loading effects (i.e spinner)
  
  return (
    ...  
  )
}

Intentionally didn’t wrap the hook in useEffect because we want to let the consumer decides when to run and how to run. I think that is important to consider. With this approach, we always control the execution and it’s pure. When I say pure, it means we know when and how it is run without going through our hook implementation itself. Which means it is easy to track when it fires off.

有意地没有在useEffect包装钩子,因为我们要让消费者决定什么时候运行,以及如何运行。 我认为这很重要。 通过这种方法,我们总是可以控制执行过程,而且它是纯净的。 当我说“ pure”时,它意味着我们无需运行钩子实现本身就知道何时以及如何运行它。 这意味着它在启动时很容易跟踪。

Demo:

演示:

User API调用用户事件(即onClick,提交表单)(💡 API Call on User Event (i.e. onClick, Form Submission))

Scenario: When the user submits the form, we want to do an API call to save the form.

场景:当用户提交表单时,我们想执行一个API调用来保存表单。

import {
  useState,
} from 'react';


export const postTodo = async (data, options) => {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos', {
    ...options,
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(data),
  });
  const responseData = await response.json();
  
  // add transformers here if needed
  
  return responseData;
};


export const usePostTodo = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);
  
  const execute = async (data, options = {}) => {
    try {
      setIsLoading(true);
      const todo = await postTodo(data, options);
      setData(todo);
      return todo;
    } catch (e) {
      setError(e);
      setIsLoading(false);
      throw e;
    }
  };
  
  return {
    isLoading,
    error,
    data,
    execute,
  };
};
import React from 'react';


export const App = () => {
  const {
    isLoading,
    data,
    execute,
  } = usePostTodo();
  
  const handleOnSubmit = async (formValues) => {
    try {
      const response = await execute(formValues);
    
      // response = response from the API call
      // redirect if needed using the `response` or do anything
    } catch () {}
  };
  
  return (
    ...  
  )
};

The pattern for doing a POST API call is similar also with how we did the GET API call. They both have the hook and non-hook version exposed so that the consumer has the option to choose between the two on which is appropriate.

进行POST API调用的模式也与我们执行GET API调用的模式相似。 它们都暴露了钩子和非钩子版本,以便消费者可以选择在合适的两者之间进行选择。

Also, the important thing on this hook implementation, if you will observe our execute method, it returns the data or it throws an error when there is. The return of data is important because there are scenarios where you need the response immediately instead of using a useEffect. You will see more about this when we go on running API calls in a serialized mode — where one response is needed to the next API call.

同样,对于此挂钩实现,重要的是,如果您将观察我们的execute方法,它将返回数据或在存在时抛出错误。 数据的返回很重要,因为在某些情况下您需要立即响应而不是使用useEffect 。 当我们继续以序列化模式运行API调用时,您将看到有关此的更多信息-在该模式下,需要对下一个API调用进行一个响应。

Demo:

演示:

Search在搜索字段上进行API调用(自动完成,表格搜索)(💡 API Call on Search Field (autocomplete, table search))

Scenario: When the user types on a search field, we want to do an API call using the value that the user entered.

场景:当用户在搜索字段中键入内容时,我们希望使用用户输入的值进行API调用。

import {
  useState,
  useCallback,
} from 'react';


export const searchTodos = async (searchValue, options) => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos?q=${searchValue}`,
    options
  );
  const data = await response.json();
  
  // add transformers here if needed
  
  return data;
}


export const useSearchTodos = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);
  
  const execute = async (searchValue, options = {}) => {
    try {
      setIsLoading(true);
      const todos = await searchTodos(searchValue, options);
      setData(todos);
      return todos;
    } catch (e) {
      setError(e);
      setIsLoading(false);
      throw e;
    }
  };
  
  return {
    isLoading,
    error,
    data,
    execute: useCallback(execute, []), // to avoid infinite calls when inside a `useEffect`
  };
};
import React, {
  useState,
} from 'react';


export const App = () => {
  const [search, setSearch] = useState('');
  const {
    isLoading,
    data,
    execute,
  } = useSearchTodos();
  
  const handleOnChange = (ev) => {
    ev.persist();
    const value = ev.target.value;


    try {
      execute(value);  
    } catch () {}
  };
  
  return (
    <input
      placeholder="Enter your query"
      onChange={handleOnChange}
      value={search}
    />
    ...  
  )
};

Demo:

演示:

Pa分页上的API调用(有限制)(💡 API Call on Paginate (with a limit))

Scenario: When the user selects a new page on the table, we want to do an API call to get the data on the selected page with the given limit. This can be applied also with other pagination options such as offset or sorting.

场景:当用户在表上选择一个新页面时,我们希望执行API调用以获取具有给定限制的所选页面上的数据。 这也可以与其他分页选项一起使用,例如偏移或排序。

import React from 'react';


export const getTodos = async (options) => {
  const page = options.page;
  const limit = options.limit;


  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos?_page=${page}&_limit=${limit}`,
    options
  );
  const data = await response.json();
  
  // add transformers here if needed
  
  return data;
}


export const useGetTodos = () => {
  const [isLoading, setIsLoading] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [data, setData] = React.useState(null);
  
  const execute = async (options = {}) => {
    try {
      setIsLoading(true);
      const todos = await getTodos(options);
      setData(todos);
      return todos;
    } catch (e) {
      setError(e);
      setIsLoading(false);
      throw e;
    }
  };
  
  return {
    isLoading,
    error,
    data,
    execute: React.useCallback(execute, []), // to avoid infinite calls when inside a `useEffect`
  };
};
import React from 'react';


export const App = () => {
  const [paginationOptions, setPaginationOptions] = React.useState({
    page: 0,
    limit: 10
  });
  const {
    isLoading,
    data,
    execute,
  } = useGetTodos();
  
  const handleOnLimitChange = (ev) => {
    ev.persist();
    const value = ev.target.value;
    setPaginationOptions((prevOptions) => ({
      ...prevOptions,
      limit: value
    }));


    try {
      execute({
        ...paginationOptions,
        limit: value
      });
    } catch (error) {}
  };
  
  const handleOnPageChange = () => {
    setPaginationOptions((prevOptions) => ({
      ...prevOptions,
      page: prevOptions.page + 1
    }));
    const nextPage = paginationOptions.page + 1;
    try {
      execute({
        ...paginationOptions,
        page: nextPage
      });
    } catch (error) {}
  };
  
  return (
    ...  
  )
};

Demo:

演示:

on序列化请求上的API调用(基于第一个API调用的第二个API调用)(💡 API Call on Serialize Requests (2nd API call based on the 1st API call))

Scenario: When the user submits the form, we want to do multiple API calls in a serialize mode to process the form values.

场景:当用户提交表单时,我们希望以序列化模式进行多个API调用以处理表单值。

import React from 'react';


export const App = () => {
  const {
    execute: executeNext00AsyncCall,
  } = executeNext00AsyncCall();
  
  const {
    execute: executeNext01AsyncCall(),
  } = useNext01ApiCall();
  
  const {
    execute: executeNext02AsyncCall(),
  } = useNext02AsyncCall();
  
  const handleOnSubmit = async (formValues) => {
    try {
      const response00 = await executeNext00AsyncCall(formValues);
      const response01 = await executeNext01AsyncCall(response00);
      const response02 = await executeNext02AsyncCall(response01);
    
      // response02 = final result response
      // redirect if needed using the response or do anything you want
    } catch (error) {}
  };
  
  return (
    ...  
  )
};

The alternative of this is using the useEffect pattern, but I think it is incorrect to do that when the request is coming from a user event. What do I mean with that? Let me show you how it will look like on useEffect .

替代方法是使用useEffect模式,但是当请求来自用户事件时,我认为这样做是不正确的。 那是什么意思让我向您展示useEffect

import React from 'react';


export const App = () => {
  const {
    execute: executeNext00AsyncCall,
  } = executeNext00AsyncCall();
  
  const {
    execute: executeNext01AsyncCall(),
  } = useNext01ApiCall();
  
  const {
    execute: executeNext02AsyncCall(),
  } = useNext02AsyncCall();
  
  const [isSubmitting, setIsSubmitting] = React.useState(false); // or use formik
  
  const handleOnSubmit = async (formValues) => {
    setIsSubmitting(true);
  };
  
  useEffect(() => {
    if (!isSubmitting) {
      return;
    }
    
    try {
      const response00 = await executeNext00AsyncCall(formValues);
      const response01 = await executeNext01AsyncCall(response00);
      const response02 = await executeNext02AsyncCall(response01);
    
      setIsSubmitting(false);
      // response02 = final result response
      // redirect if needed using the response or do anything you want
    } catch () {}
    
  }, [isSubmitting]);
  
  return (
    ...  
  )
};

When we do the useEffect approach, we need to add an extra flag state to teach our useEffect how it will react intellectually because if not, it will run infinite times. The inconvenience also on this approach is the resetting of the state value which will probably be tedious as the app grows big.

当我们使用useEffect方法时,我们需要添加一个额外的标志状态来告诉我们useEffect如何智能地做出React,因为如果没有,它将运行无限次。 这种方法的不便之处还在于状态值的重置,这可能会随着应用程序的增大而变得乏味。

Here’s another example:

这是另一个例子:

import React from 'react';


export const App = () => {
  const {
    data: data00,
    error: error00,
    execute: executeNext00AsyncCall,
  } = executeNext00AsyncCall();
  
  const {
    data: data01,
    error: error01,
    execute: executeNext01AsyncCall(),
  } = useNext01ApiCall();
  
  const {
    data: data02,
    error: error02,
    execute: executeNext02AsyncCall(),
  } = useNext02AsyncCall();
  
  const [isSubmitting, setIsSubmitting] = React.useState(false); // or use formik
  
  const handleOnSubmit = (formValues) => {
    setIsSubmitting(true);
    
    executeNext00AsyncCall(formValues);
  };
  
  useEffect(() => {
    if (data00 && !data01) { // if first API call have data and second API call doesn't have data yet
      try {
        executeNext01AsyncCall(data00);  
      } catch (error) {}
    }
  }, [
    data00,
    data01,
    executeNext01AsyncCall,
  ]);
  
  useEffect(() => {
    if (data01 && !data02) { // if second API call have data and third API call doesn't have data yet
      try {
        executeNext02AsyncCall(data01);  
      } catch (error) {}
    }
  }, [
    data01,
    data02,
    executeNext02AsyncCall,
  ]);
  
  useEffect(() => {
    if (data01 && data02 && data03) { // all calls have data
      setIsSubmitting(false);
      // redirect if needed using the response or do anything you want
    }
  }, [
    data01,
    data02,
    data03
  ]);
  
  return (
    ...  
  )
};

It’s hard to track states, understand the flow or sequence and the error handling is hard. To handle errors using this approach, you have to listen to the error object on each hook and put it inside the useEffect. This is also prone to infinite re-render. Prone to multiple API requests if not handled correctly. One incorrect flag and then there you go sending multiple API requests.

很难跟踪状态,了解流程或顺序,并且很难进行错误处理。 要使用这种方法处理错误,您必须侦听每个钩子上的错误对象,并将其放在useEffect 。 这也易于无限重渲染。 如果处理不当,容易出现多个API请求。 一个不正确的标志,然后您就可以发送多个API请求。

You might ask, why did I just put everything in one hook? That’s a good question. That will actually work and that is much better than with all these useEffect . That approach is like putting a lot inside of things into a single plastic. We’re mixing a lot of things and putting a lot of responsibility to that hook. It will be hard to test it and make sure it is doing what it is supposed to do so. It’s hard to reason that. Another reason why I didn’t do that is I want the API hooks to be used interdependently with each other. For example, I want to use API hook A to this page but I don’t want to fire API hook B. It’s hard to do composition at a bigger level when all of those operations are inside a single hook.

您可能会问,为什么我只是将所有内容放在一起? 这是个好问题。 这实际上将起作用,并且比所有这些useEffect 。 这种方法就像将很多东西放入单一塑料中。 我们正在混合很多东西,并为此承担了很多责任。 对其进行测试并确保它正在执行预期的工作将非常困难。 很难推理。 我不这样做的另一个原因是,我希望API挂钩彼此相互依赖地使用。 例如,我想在此页面上使用API​​钩子A,但我不想触发API钩子B。当所有这些操作都在单个钩子中时,很难在更大的层次上进行合成。

Demo:

演示:

Together整合在一起(🤖 Bringing It All Together)

There are a lot of use-cases I’m not yet aware of and so this list can’t provide all of the examples but I think this provides good progress and pattern on how you will work on your API calls on React.

我尚不知道很多用例,因此该列表无法提供所有示例,但我认为这为您如何在React上进行API调用提供了良好的进展和模式。

If you will observe most of the examples, it doesn’t have a lot of useEffect implementations. The reason is, I did encounter a lot of infinite loops already, implemented a lot through it, and got to the point where I realized that there are other options out there. Other options that existed way before this comes up. That option was the non-hook approach. It’s always been there. But, we did substitute it with these new approaches.

如果您将观察大多数示例,则其中没有很多useEffect实现。 原因是,我确实已经遇到了很多无限循环,并通过它进行了很多实现,直到达到我意识到还有其他选择的地步。 在此之前存在的其他选项。 该选择是非挂钩方法。 一直在那里。 但是,我们确实用这些新方法替代了它。

I wonder if how many approaches can we solve with the basic approaches? I’m not saying one is good and one is bad. I don’t know a lot to say that one is good and one is bad. I’m trying to see if do I/we really have the right understanding of the fundamentals?

我想知道基本方法能解决多少种方法? 我并不是说一个是好的,一个是坏的。 我不知道说一个好是坏。 我正在尝试查看我/我们是否对基本原理有正确的理解?

At the end of the day, this isn’t a silver bullet guide on how to perfectly do your API call on React but this will help you strategize or have another option on how to do it in another approach. Try it out and let me hear your experience with it!

归根结底,这并不是关于如何完美地在React上进行API调用的灵丹妙药指南,但这将帮助您制定策略或以另一种方法选择如何做。 试试看,让我听听您的经验!

Did I miss something? Comment down and let’s work on that!

我错过了什么? 评论一下,让我们继续努力吧!

Thank you for reading. I hope this will help you on your journey! ❤️

感谢您的阅读。 希望这对您的旅途有所帮助! ❤️

翻译自: https://medium.com/weekly-webtips/patterns-for-doing-api-calls-in-reactjs-8fd9a42ac7d4

reactjs调用带参方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值