2

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.com/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.com/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.

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. 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 Commented Sep 8, 2020 at 14:36

2 Answers 2

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.com/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;

Sign up to request clarification or add additional context in comments.

1 Comment

Oh Ok I understand. So I guess I will need to implement a store for my API calls. Thanks for the explanation.
2

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;
}, []);

Edit cranky-silence-rdyxx

1 Comment

Thanks for the explanation but I will probably just create a store for that instead of using useRef.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.