0

hi I was studying react and I've got something curious when I made this example. this code is that sending get request to newsapi, and receive the result. and I used custom hook for it.

// *** this is where I use custom hook. "usePromise" is custom hook. *** //
function NewsList({ category }) {
  const [loading, response, error] = usePromise(() => {
    console.log("how many will it run?")
    const query = category === 'all' ? '' : `&category=${category}`;
    return axios.get(
      `https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=044c3c345f28417380cd3ea946ac8641`
    );
  },[category]); 

  console.log(loading);

...

I wrote console.log("how many will it run?") to check how many this function will run. and also console.log(loading) to check how many times loading will be changed

// *** this is custom hook's code *** //
export default function usePromise(promiseCreator, deps) {
  const [loading, setLoading] = useState(true);
  const [response, setResponse] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const process = async () => {
      await promiseCreator()
        .then((res) => {
          setResponse(res);
        })
        .catch((e) => {
          console.log('catch');
          setError(e);
        });
      setLoading(false);
    };
    process();
  }, deps);
  return [loading, response, error];
}

I wrote console.log('catch') to check how many this promise function will start. (Btw, cause of sending too many request, this code appears "error" when I send request)

// *** this is the console when I run this project on browser *** //
NewsList.jsx:31 true
NewsList.jsx:24 how many will it run?
NewsList.jsx:24 how many will it run?
usePromise.js:15 catch
NewsList.jsx:31 false
usePromise.js:15 catch
NewsList.jsx:31 false

as you can see. usePromise is excecuted twice. I knew loading will be consoled twice, "true" and "false" cause the function in usePromise is async. but I don't know why usePromise is executed twice. I expected it will be excecuted once when it is declared.

if you know the answer please comment for me. it would help me a lot thank you

General Grievance
  • 4,555
  • 31
  • 31
  • 45
김경한
  • 57
  • 1
  • 7
  • You should probably log `category` too in the "how many will it run?" line. – AKX Apr 04 '22 at 10:09
  • As an aside: you might want to look at https://swr.vercel.app/ for a replacement for your `usePromise` hook – it's made for things like this. – AKX Apr 04 '22 at 10:10
  • 3
    Are you using a ``? If yes, then Strict mode has been designed to run effects twice. – Jakub Kotrs Apr 04 '22 at 10:11
  • @김경한 Did you click through to see the `useSWR()` hook? – AKX Apr 04 '22 at 10:18
  • Strict mode is for detecting bugs you may have inside your effects but also for detecting other issues with your code that would prevent you to easily upgrade to the next version of React. Your effects should be resilient to it, calling them twice should not cause bugs in your code, even it means your API is called twice. In production, it doesn't do anything, it's a development only tool. – Jakub Kotrs Apr 04 '22 at 10:26
  • @김경한 Yes, but you _could_ use `useSWR()` instead of writing your own `usePromise()`. – AKX Apr 04 '22 at 10:27
  • In this case, it's trying to point out you have an issue in your effect that it doesn't have a cleanup phase for example. If the effect has been cleaned up because the dependencies changed or because the component unmounted, you would have an issue (like calling setState on an unmounted component). – Jakub Kotrs Apr 04 '22 at 10:27
  • `useSWR` or any other similar tool is offered by AKX so that you don't have to reinvent the wheel, it's been solved before. But it's good for your understanding that you are trying it! – Jakub Kotrs Apr 04 '22 at 10:28
  • I'll change my comments to an answer, just to make it clean here :) – Jakub Kotrs Apr 04 '22 at 10:36

1 Answers1

7

Are you using a <StrictMode>? If yes, then Strict mode has been designed to run effects twice.

Strict mode is for detecting bugs you may have inside your effects but also for detecting other issues with your code that would prevent you to easily upgrade to the next version of React. Your effects should be resilient to it, calling them twice should not cause bugs in your code, even it means your API is called twice. In production, it doesn't do anything, it's a development only tool.

In this case, it's trying to point out you have an issue in your effect that it doesn't have a cleanup phase for example. If the effect has been cleaned up because the dependencies changed or because the component unmounted, you would have an issue (like calling setState on an unmounted component).

Example of cleanup:

useEffect(() => {
  // use local flag
  let cleanup = false

  const process = () => {
    Promise.resolve(true).then(() => {
      // check if cleanup has been run
      if (!cleanup) {
        setState(true)
      }
    })
  }

  process()

  // set the flag
  return () => {
    cleanup = true
  }
}, [])
Jakub Kotrs
  • 5,823
  • 1
  • 14
  • 30