Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Solved] Uncaught Error: Maximum update depth exceeded (using axios post) #542

Closed
cseas opened this issue Jul 21, 2020 · 7 comments
Closed

Comments

@cseas
Copy link

cseas commented Jul 21, 2020

Bug report

Description / Observed Behavior

Code keeps getting called repeatedly eventually ending in a Maximum update depth error.

Expected Behavior

Call should work as expected. I'm just trying to make a basic axios post request.

Repro Steps / Code Example

// App.tsx
import React from 'react';
import https from "https";
import axios from "axios";
import useSWR from "swr";

type myInput = {
  myInputData: string;
};

export function App() {
  const { data, error } = useSWR(
    [
      "/api/myEndpoint",
      {
        myInputData: "12345",
      },
    ],
    myFetcher
  );

 console.log("SWR response:", data);
 
  return <></>
}

const myFetcher = async (url: string, input: myInput) => {
  const agent = new https.Agent({
    // custom agent config
  });

  const response = await axios({
    method: "post",
    url: url,
    data: input,
    httpsAgent: agent,
  });

  return response.data;
};

Additional Context

SWR version: 0.2.3

@sergiodxa
Copy link
Contributor

Objects are not supported as part of a key, they are going to be regenerated on every render, instead try sending more values in the array or if possible move the definition of the object to outside the component (in your example is possible but most probably it will not when using it in an app)

@cseas
Copy link
Author

cseas commented Jul 21, 2020

@sergiodxa Thank you! Looks like that was the issue. I passed the individual POST request input fields like this:

useSWR(
    [ "/api/myEndpoint", "12345", "abcd" ],
    myFetcher
);

Then I modified the fetcher to take those individual values and construct the object to send as input before making the axios call. However, this still doesn't ideally solve my use case. I'd like to write an axios POST based fetcher that can take an object and a url and just make a POST request to that url using the object as input.

Here's the fetcher I've written:

async function axiosPOST(urlEndpoint, inputData) {
  const agent = new https.Agent({
    // custom agent config
  });

  const response = await axios({
    method: "post",
    url: urlEndpoint,
    data: inputData,
    httpsAgent: agent,
  });

  return response.data;
}

Now if SWR doesn't support passing in objects then I'll need to write a separate fetcher for every API call that constructs the input object inside the fetcher before passing it to axios. Any way we can make a general fetcher like the above work with SWR?

@cseas
Copy link
Author

cseas commented Jul 21, 2020

For anyone who stumbles into the same error, I'll leave a minimal working code example here for axios post with typescript:

// Call.tsx
import React from "react";
import axios from "axios";
import useSWR from "swr";

type myInput = {
  id: string;
  name: string;
};

const inputData = { id: "12345", name: "abcd" };

export function Call() {
  const { data, error } = useSWR(
    ["/api/myEndpoint", inputData],
    axiosPostFetcher
  );

  if (data) console.log("SWR response:", data);
  if (error) console.log("SWR Error:", error);
  if (!data) console.log("SWR undefined");

  return <></>;
}

async function axiosPostFetcher(urlEndpoint: string, inputData: myInput) {
    const response = await axios({
      method: "post",
      url: urlEndpoint,
      data: inputData,
    });
    return response.data;
}

The problem with this approach is that your inputData for the post request always needs to be outside the component where useSWR() is getting called. This probably wouldn't work for most real use cases. So the only alternative seems to be passing in individual values to useSWR() and constructing the needed object inside the fetcher, which means you'll have to write a different fetcher for every different url endpoint, which seems counter-productive to me. It would be great if there was a way to make the above fetcher work without isolating the inputData outside the component where it's needed for the post call.

@sergiodxa
Copy link
Contributor

If you want to use objects with a single fetcher function instead of a custom one per useSWR call you can serialize it like this:

function fetcher(url: string, serializedParams: string) {
  const params = JSON.parse(serializedParams);
  // code here
}

useSWR(["/api", JSON.stringify({ token: userToken })], { fetcher })

Something like that

But I'm not sure how performant that could be, you are going to serialize and deserialize a lot.

If you don't enable Suspense on SWR, you may be able to also use React.useMemo to memoize the object and pass it directly, but this is also not 100% secure.

@cseas
Copy link
Author

cseas commented Jul 21, 2020

@sergiodxa Thanks a lot! The JSON serialize/deserialize example works perfectly. I'll mark the issue as resolved.

Documentation improvement suggestions for the SWR team:

  • The axios example in the docs gives no hint of how to use a post call. I found out from this comment that useSWR() passes arguments to the fetcher.
  • The arguments and object passing sections of the docs are probably relevant here. The axios example should perhaps mention somewhere that these sections should be referred for POST calls.
  • I looked at the axios typescript example but that seems too overkill for making a simple axios call. A "getting started" code, something like the one I posted above, is missing from documentation and would probably help beginners.

@cseas cseas closed this as completed Jul 21, 2020
@cseas cseas changed the title Uncaught Error: Maximum update depth exceeded (using axios post) [Solved] Uncaught Error: Maximum update depth exceeded (using axios post) Jul 21, 2020
@sergiodxa
Copy link
Contributor

I just realized you are using SWR to do a POST, except when using GraphQL where you need to use POST to fetch data (usually), the recommended way to do a POST request is to don't use SWR, instead run your request in an effect or event handler, SWR can and will run multiple times and a POST is usually used to create new resources, using them combined will cause unwanted side-effects where you are creating new resources even if you don't need them.

@techandmedia
Copy link

Hi,
I also use Post method to get the data so the URL is clean (no long query param in the browser bar).

Also, still no built-in support for object params? I use object a lot for params, it's very handy when one of the value change, I can re-trigger getting the new data by spread object {...previosParams, id: newID}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants