最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Memoize API response using useCallback hook - Stack Overflow

programmeradmin1浏览0评论

I need to fetch an API in my React project when I click on a button, but if I click again it should not redo the API call but retrieving the latest fetched data instead.

I know the hook useCallback that can memoize function. So I just put my API call inside an useCallback:

  const memoizedApiResponse = React.useCallback(async () => {
    console.log("fetch is called again :(");
    let response = await fetch(".2/users?order=desc&sort=reputation&site=stackoverflow");

    return await response.text();
  }, []);

So I just call this memoized function when I click on the button:

import React from "react";

const App = () => {
  const [apiResponse, setApiResponse] = React.useState(undefined);

  const memoizedApiResponse = React.useCallback(async () => {
    console.log("fetch is called again :(");
    let response = await fetch(
      ".2/users?order=desc&sort=reputation&site=stackoverflow"
    );

    return await response.text();
  }, []);

  const updateApiResult = async () => {
    const apiResponse = await memoizedApiResponse();
    setApiResponse(apiResponse);
  };

  return (
    <div className="App">
      <button onClick={updateApiResult}>fetch API</button>
      <p>{apiResponse}</p>
    </div>
  );
};

export default App;

But sadly on each click the request is send (you can see the message in the console). Have you an idea how to memoize the request response using useCallback?

Here is a link to the codesandbox.

I need to fetch an API in my React project when I click on a button, but if I click again it should not redo the API call but retrieving the latest fetched data instead.

I know the hook useCallback that can memoize function. So I just put my API call inside an useCallback:

  const memoizedApiResponse = React.useCallback(async () => {
    console.log("fetch is called again :(");
    let response = await fetch("https://api.stackexchange./2.2/users?order=desc&sort=reputation&site=stackoverflow");

    return await response.text();
  }, []);

So I just call this memoized function when I click on the button:

import React from "react";

const App = () => {
  const [apiResponse, setApiResponse] = React.useState(undefined);

  const memoizedApiResponse = React.useCallback(async () => {
    console.log("fetch is called again :(");
    let response = await fetch(
      "https://api.stackexchange./2.2/users?order=desc&sort=reputation&site=stackoverflow"
    );

    return await response.text();
  }, []);

  const updateApiResult = async () => {
    const apiResponse = await memoizedApiResponse();
    setApiResponse(apiResponse);
  };

  return (
    <div className="App">
      <button onClick={updateApiResult}>fetch API</button>
      <p>{apiResponse}</p>
    </div>
  );
};

export default App;

But sadly on each click the request is send (you can see the message in the console). Have you an idea how to memoize the request response using useCallback?

Here is a link to the codesandbox.

Share Improve this question asked Sep 8, 2020 at 14:31 johannchopinjohannchopin 14.9k11 gold badges62 silver badges121 bronze badges 2
  • 1 Are you trying to keep the function from being created multiple times or keep the data from being fetched multiple times. useCallback saves you from creating a function every render. It doesn't memoize the data that it might return. You will still keep calling the memoized function. – zero298 Commented Sep 8, 2020 at 14:36
  • 1 The simplest solution would be to check if the apiResponse variable is already filled inside the updateApiResult – 0stone0 Commented Sep 8, 2020 at 14:36
Add a ment  | 

2 Answers 2

Reset to default 4

memoizedApiResponse is not what its name implied (a memoized API response), it is a memoized function that will get you data from an API. You are just saving yourself the creation of the function that will get you the data, not the actual retrieval of the data.

Additionally, that useCallback probably isn't saving you much. I suggest you read this article: When to useMemo and useCallback it really helps with understanding what some of these memoizing hooks do and when they actually help you.

I think you want this

import React from "react";

const App = () => {
  const [apiResponse, setApiResponse] = React.useState(undefined);
  
  const getData = async () => {
    if (apiResponse) {
      return apiResponse;
    }
  
    const response = await fetch("https://api.stackexchange./2.2/users?order=desc&sort=reputation&site=stackoverflow");
    const data = await response.text();
    setApiResponse(data);
  };

  return (
    <div className="App">
      <button onClick={getData}>fetch API</button>
      <p>{apiResponse}</p>
    </div>
  );
};

export default App;

useCallback doesn't memoize responses, it memoizing the function declaration between renders.

In your case you only need to check if you already have a result, to fix your code and keep the useCallback you need to use a reference:

const textRef = useRef(null);

const memoizedApiResponse = React.useCallback(async () => {
  if (textRef.current === null) {
    console.log("fetch is called");
    textRef.current = true;
    let response = await fetch("...");

    const text = await response.text();
    textRef.current = text;
    return text;
  }

  return textRef.current;
}, []);

发布评论

评论列表(0)

  1. 暂无评论