angelle-sw/use-axios-client

Infinite loop with useLazyAxio

Closed this issue · 2 comments

HI, I made a simple example to reproduce the bug : https://codesandbox.io/s/pensive-sky-0zhv5
I have an accordion and I would like to load its content only when the accordion is open.
So, to do that I call the getData from the useLazyAxios inside an useEffect hook and it causes an infinite loop.

I don't know if I miss something or this is a bug ?

Here is the code

import React from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import { useLazyAxios } from "use-axios-client";

import "./styles.css";

const instance = axios.create({
  withCredentials: true
});

function App() {
  const [open, setOpen] = React.useState(false);
  const [getData, { data, loading, cancel }] = useLazyAxios({
    url: "https://jsonplaceholder.typicode.com/todos/",
    axiosInstance: instance
  });

  React.useEffect(() => {
    if (open && !loading) {
      console.log("getData");
      getData();
    }
  }, [getData, open, loading]);

  return (
    <details open={open}>
      <summary onClick={() => setOpen(oldValue => !oldValue)}>
        Click to toggle accordion
      </summary>
      <div>
        {!loading && data && (
          <h2>Load the data only if the accordion is open</h2>
        )}
      </div>
    </details>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
zxqx commented

Appreciate the repro case. So I see three separate issues here - the first one is in the useEffect body:

React.useEffect(() => {
  if (open && !loading) {
    console.log("getData");
    getData();
  }
}, [getData, open, loading]);

I don't think you want to call getData when loading is false, because that means as soon as the request completes, it will be retriggered again (and again).

For clarity, this will happen:

  1. Accordion is opened
  2. useEffect function is triggered
  3. getData is called and loading is set to true
  4. Network request completes and loading is set to false
  5. Steps 2-4 loop

I think what you actually want is:

React.useEffect(() => {
  if (open) {
    console.log("getData");
    getData();
  }
}, [getData, open]);

However, there is a separate second issue here that could be considered a bug in use-axios-client - since getData is not memoized with useCallback, it cannot be passed as a dependency to useEffect, as it gets freshly created on every render. This has been fixed and is now available in v1.0.1.

The last issue is related to the <details> HTML element open state getting out of sync with your local component open state, which you can find some more details on in this thread.

Updated your codesandbox using use-axios-client@v1.0.1: https://codesandbox.io/s/festive-glade-imxd6

Let me know if you have any other issues. Appreciate the help!

@zakangelle thank you very much for the new version which solvles the bug, I remove the loading dependency too.
For the accordion bug, you absolutely right, we have to put e.preventDefault() to synchro the component with its react state.