Code keeps getting called repeatedly eventually ending in a Maximum update depth error.
Call should work as expected. I'm just trying to make a basic axios post request.
// 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;
};
SWR version: 0.2.3
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)
@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?
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.
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.
@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:
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.
Most helpful comment
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)