This hook for sending API calls works perfectly fine. It has been working fine for last 6 months.
I have no clue why it is failing for a single route.
All the errors returned from the API have the same structure :
[{ message: "Invalid username" }, { message: "Invalid password" }]
Error
Error: Objects are not valid as a React child (found: Error: [object Object]).
If you meant to render a collection of children, use an array instead.
Hook
export const useRequest = ({ url, method, body, onSuccess }) => {
const [errors, setErrors] = useState(null);
const makeRequest = async (e) => {
try {
setErrors(null);
const { data } = await axios[method](url, { ...body });
if (onSuccess) {
// will invoke callback function that is passed
onSuccess(data);
}
return data;
} catch (err) {
if (err && err.response?.data && err.response?.data.errors) {
// figuring out what is going wrong!
err.response?.data.errors
.map((each) => console.log(typeof each.message)); // returns string
setErrors(
// <== SHOWS ERROR HERE !!!!! WHY??!!
<ul className="errors-list">
{err.response?.data.errors.map((each, index) => (
<li key={index}>{each.message}</li>
))}
</ul>
);
}
}
};
return { makeRequest, errors };
};
This is the first time it is showing error, it has never in last 6 months behaved like this.
Please help!
Related
I am using NextJS andgetServerSideProps to get some data from the db with Prisma. In the development envirnoment, I had no issue. But when deployed with vercel, I am constantly running into issues.
Here is what I've done:
I created the API route that get the Toto lists from the db. In this function I simply return the shopping list array as following:
import prisma from '../../../lib/prisma'
export default async function handler(req, res) {
const { userId } = req.query;
if (req.method === 'GET') {
try {
const shoppingLists = await prisma.List.findMany({ where: { userId: userId[0] }});
res.status(200).send(shoppingLists);
}
catch (error) {
console.log(error);
res.status(500).json({ message: 'Something went wrong. Please try again'});
}
}
else {
res.status(500).json({message: 'Invalid method requested!'});
}
}
After this, I created a separate folder called abstraction layer where I do all the DB interactions. I am using axios. In this function I fetch the data and return it as list.data;
// Get all lists
export const getAllLists = async userId => {
try {
const lists = await axios.get(`https://next-shopping-list.vercel.app/api/get-all-lists/${userId}`, {
headers: { "Accept-Encoding": "gzip,deflate,compress" } // This is taken from internet because I had other errors "invalid file"
});
return lists.data;
}
catch (error) {
console.log('Abstraction layer error: ', error);
return 'Something went wrong. Please try again later';
}
}
// The component (Dashboard) that will contain get server side props and the return()
3. Here it comes the problem. I use SSR because I want to also protect this page. In this function I use the function getAllLists from the "abstraction layer" and return the shopping lists with the 'lists' prop...
export const getServerSideProps = async context => {
// get sessions with added user Id in the session object
const session = await requireAuthentication(context);
// Get all lists
const shoppingLists = await getAllLists(session?.user.userId);
console.log(shoppingLists);
if (!session) {
return {
redirect: {
destination: '/signup',
permanent: false
}
}
}
else {
return {
props: {
newSession: session,
lists: shoppingLists
}
}
}
}
After creating the component, I start to get errors trying to map over the array of lists, throwing two errors:
The 'props.lists.map()...' is not a function.
Cannot read properties of undefined (reading 'length')
const Lists = props => {
const router = useRouter();
console.log(props.lists);
const handleDeleteList = async listId => {
const status = await deleteList(listId);
console.log(status);
if (status.status === 201) {
router.replace(router.asPath);
}
}
const handleCheckList = async listId => router.push(`/list-items/${listId}`);
// New try re
return (
<article>
{props.lists.length > 0 && props.lists.map(list => (
<div key={list.id}>
<div className='flex justify-between my-2 cursor-pointer p-2 items-center'>
<p>{ list.title }</p>
<div className='flex gap-3'>
<AiOutlineDelete size={30} onClick={() => handleDeleteList(list.id)}/>
<AiOutlineEye size={30} onClick={() => handleCheckList(list.id)} />
</div>
</div>
</div>
))}
</article>
)
}
export default Lists
I don't understand what have I done wrong... In development envirnoment it worked perfectly....
{/* List is empty and display empty message // Else show list */}
{props.lists && <Lists lists={props.lists}/>}
{props.lists.length === 0 &&
<p className="mt-2 text-2xl font-extralight">No lists created yet.</p>}
</aside>
The component is expecting props.lists to be an array, looks like initially props.list doesn't have an array. That's why it is causing errors as props.lists.length, and the props.lists.map is only available if props.lists is an array.
So before calling it, make sure props.list is an array, you can use optional chaining (props?.lists.?length)
I'm working with Next.js, I tried accessing data but got this error:
Error: Error serializing `.profileData` returned from `getStaticProps` in "/profile/[slug]".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
My code:
import { getAllBusinessProfiles } from '../../lib/api';
const Profile = ({ allProfiles: { edges } }) => {
return (
<>
<Head>
<title>Profile</title>
</Head>
<Hero />
<section>
{edges.map(({ node }) => (
<div key={node.id}>
<Link href={`/profile/${node.slug}`}>
<a> {node.businessInfo.name} </a>
</Link>
</div>
))}
</section>
</>
);
}
export default Profile;
export async function getStaticProps() {
const allProfiles = await getAllBusinessProfiles();
return {
props: {
allProfiles
}
};
}
getAllBusinessProfiles from api.js:
const API_URL = process.env.WP_API_URL;
async function fetchAPI(query, { variables } = {}) {
const headers = { 'Content-Type': 'application/json' };
const res = await fetch(API_URL, {
method: 'POST',
headers,
body: JSON.stringify({ query, variables })
});
const json = await res.json();
if (json.errors) {
console.log(json.errors);
console.log('error details', query, variables);
throw new Error('Failed to fetch API');
}
return json.data;
}
export async function getAllBusinessProfiles() {
const data = await fetchAPI(
`
query AllProfiles {
businessProfiles(where: {orderby: {field: DATE, order: ASC}}) {
edges {
node {
date
title
slug
link
uri
businessInfo {
name
title
company
image {
mediaItemUrl
altText
}
highlight
phone
city
country
facebook
instagram
email
website
profiles {
profile
profileInfo
}
extendedProfile {
title
info
}
}
}
}
}
}
`
);
return data?.businessProfiles;
};
What could be the error here? I used the getStaticProps method on Next.js but got the error above instead. Please, check. Thanks.
The error:
Server Error
Error: Error serializing .profileData returned from getStaticProps in "/profile/[slug]".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.
I don't know what could cause this though.
Add JSON.stringify when calling an asynchronous function that returns an object.
Try modifying your getStaticProps function like this.
export async function getStaticProps() {
const profiles = await getAllBusinessProfiles();
const allProfiles = JSON.stringify(profiles)
return {
props: {
allProfiles
}
};
}
The JSON.stringify() method converts a JavaScript object or value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.
Source: MDN
I had this issue using Mongoose and Next.js.
To solve it: I switched from convert require to import then wrapped my result in JSON.parse(JSON.stringify(result));.
Good: import mongoose from 'mongoose';
Bad: const mongoose = require('mongoose');
I had the same serialization error when accessing a Vercel system environment variable in getStaticProps.
Using JSON.stringify did not do the trick, but String() worked. My code:
export async function getStaticProps() {
const deploymentURL = String(process.env.NEXT_PUBLIC_VERCEL_URL);
return {
props: {
deploymentURL,
},
};
}
Thanks to this GitHub issue for the inspiration
I had the same issue when I was working with redux with next js and the reason was one of the fields in the default state I set it to undefined. Instead I used null:
const INITIAL_STATE = {
products: [],
loading: false,
error: undefined,
cart: [],
};
error:undefined was causing the error. Because "undefined" cannot be serialized:
export async function getStaticProps() {
const allProfiles = await getAllBusinessProfiles();
return {
props: {
allProfiles
}
};
}
you are returning "allProfiles" which is the result of async getAllBusinessProfiles() which is either returning undefined, error or one of the fields of the returned object is undefined. "error" object is not serializable in javascript
Instead of using undefined, you have to use null as the value for your variables.
Note that the error shows you exactly which variable is using undefined as its value. Just modify its value to be null.
The value 'undefined' denotes that a variable has been declared, but hasn't been assigned any value. So, the value of the variable is 'undefined'. On the other hand, 'null' refers to a non-existent object, which basically means 'empty' or 'nothing'.
Source: [1]
I was having the same issue while trying to find a match in the array of data using the id. The issue I had was the items in the array had ids which were numbers while the value I was getting from params was a string. So all i did was convert the number id to a string to match the comparison.
export async function getStaticProps({ params }) {
const coffeeStore = coffeeStoreData.find(
(store) => store.id.toString() === params.slug[0]
);
return {
props: {
coffeeStore,
},
};
}
install a package called babel-plugin-superjson-next and superjson and added a .babelrc file with these contents:
{
"presets": ["next/babel"],
"plugins": ["superjson-next"]
}
see this topic : https://github.com/vercel/next.js/discussions/11498.
I had a similar problem too where I was fetching data through apollo directly inside of getStaticProps. All I had to do to fix the error was add the spread syntax to the return.
return {
props: {
data: { ...data }
}
}
return { props: { allProfiles: allProfiles || null } }
In getStaticProps() function, after fetching your data it will be in json format initially, but you should change it as follow:
const res = await fetch(`${APP_URL}/api/projects`);
const data = JSON.parse(res);
now it will work.
When you call api you should use try catch. It will resolve error.
Example:
import axios from "axios";
export const getStaticProps = async () => {
try {
const response = await axios.get("http:...");
const data = response.data;
return {
props: {
posts: data
}
}
} catch (error) {
console.log(error);
}
}
Hope help for you !
put res from API in Curly Brackets
const { res } = await axios.post("http://localhost:3000/api", {data})
return { props: { res } }
try this, it worked for me:
export async function getStaticProps() {
const APP_URL = process.env.PUBLIC_NEXT_WEB_APP_URL;
const res = await fetch(`${APP_URL}/api/projects`);
const projects = await res.json();
return {
props: {
projects: projects?.data,
},
};
}
I have a component in react that fetches a story that a user wrote from the backend. I then store the json in the storyState and when I log it, the needed properties like "title" are in there, but when I try to access the property title, I get this error:
TypeError: Cannot read properties of undefined (reading 'title')
Here is the code:
import { useState, useEffect } from 'react';
import styled from 'styled-components';
export const StoryPage = () => {
const [storyState, setStoryState] = useState({
story: {},
isLoaded: false,
error: null,
});
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const id = urlParams.get('storyid');
useEffect(() => {
fetch('http://localhost:5000//getstory?storyid=' + id)
.then((response) => {
console.log('response is', response);
if (response.status !== '200') {
let err = Error;
err.message = 'Invalid response code: ' + response.status;
setStoryState({ error: err });
}
return response.json();
})
.then((json) => {
console.log('json is', json);
setStoryState({
story: json.story,
isLoaded: true,
});
});
}, []);
console.log('storystate.story', storyState.story);
return (
<>
<h1>test</h1>
<h1>{storyState.story.title}</h1>
</>
);
};
export default StoryPage;
I checked StackOverflow and tried using JSON.parse, which didn't work unfortunately.
When I do < h1 >{storyState.story}</ h1 > I get the whole object in string form with all the properties as expected.
Here is my console:
Issue is at this line -
if (response.status !== "200") {
}
It should be 200 instead of "200". You can check the console.log("response is", response);, it's numeric 200 not string.
Note: Always use the below construct to setState for objects -
{ ...previousState, { newkey: newvalue } }
The above uses the previous values of object and replaces with new values.
In the very first render, data is not available, hence you have to render
<h1>{storyState.story.title}</h1> only when the data is fetched and available so add an if statement in the render or you can use optional chaining too
<h1>{storyState?.story?.title}</h1>
There could be a delay in fetching data from server, you can add a loader in it which checks if there is a value in storySTate then render the data or add another useEffect function which logs file whenever there is a change in storyState hook eg:
useEffect(() => {
console.log(storyState.story)
}, [storyState])
I have a three-check box type,
When I check any box I call refetch() in useEffect().
The first time, I check all boxes and that returns the expected data!
but for some cases "rechange the checkboxes randomly", the returned data from API is "undefined" although it returns the expected data in Postman!
So I Guess should I need to provide a unique queryKey for every data that I want to fetch
so I provide a random value "Date.now()" but still return undefined
Code snippet
type bodyQuery = {
product_id: number;
values: {};
};
const [fetch, setFetch] = useState<number>();
const [bodyQuery, setBodyQuery] = useState<bodyQuery>({
product_id: item.id,
values: {},
});
const {
data: updatedPrice,
status,
isFetching: loadingPrice,
refetch,
} = useQuery(
['getUpdatedPrice', fetch, bodyQuery],
() => getOptionsPrice(bodyQuery),
{
enabled: false,
},
);
console.log('#bodyQuery: ', bodyQuery);
console.log('#status: ', status);
console.log('#updatedPrice: ', updatedPrice);
useEffect(() => {
if (Object.keys(bodyQuery.values).length > 0) {
refetch();
}
}, [bodyQuery, refetch]);
export const getOptionsPrice = async (body: object) => {
try {
let response = await API.post('/filter/product/price', body);
return response.data?.detail?.price;
} catch (error) {
throw new Error(error);
}
};
So after some elaboration in the chat, this problem can be solved by leveraging the useQuery key array.
Since it behaves like the dependency array in the useEffect for example, everything that defines the resulted data should be inserted into it. Instead of triggering refetch to update the data.
Here the key could look like this: ['getUpdatedPrice', item.id, ...Object.keys(bodyQuery.values)], which will trigger a new fetch if those values change and on initial render.
I have a test where if /endpoint1 fails, an error message should be displayed. The test passes however I get the following error from React Testing Library:
Warning: An update to HomePage inside a test was not wrapped in act(...).
Here's the snipper for clarity. You can see I'm using SWR to send requests. I'm mocking requests using Mock Service Worker.
export default function App() {
const { data: data1, error: error1 } = useSwr("/endpoint1")
const { data: data2, error: error2 } = useSwr("/endpoint2")
if (error1 || error2) {
return <Error />
}
return <Success />
}
The error is present because /endpoint2 resolves after /endpoint1, causing the component to rerender. I believed that cancelling the second request once the first one errors would work. I made a wrapper around useSwr() that used AbortController() and exposed an cancel() function however the same error would occur with this snippet:
export default function App() {
const { data: data1, error: error1, cancel: cancel1 } = useCancellableSwr(
"/endpoint1"
)
const { data: data2, error: error2, cancel: cancel2 } = useCancellableSwr(
"/endpoint2"
)
if (error1 || error2) {
if (!data1 || !error1) cancel2()
if (!data2 || !error2) cancel1()
return <p>Error</p>
}
return <p>Success</p>
}
For some reason, even after cancelling one endpoint, it still resolves and causes a rerender. Am I missing something here? Thanks
Edit:
Here's the failing test:
test("An error shows if unable to fetch /endpoint1", async () => {
mockServer.use(
rest.get("/endpoint1", (_, res, ctx) => res(ctx.status(500)))
)
mockServer.use(
rest.get("/endpoint2", (_, res, ctx) => res(ctx.status(200)))
)
render(<App />)
const error = await screen.findByText("Error")
expect(error).toBeInTheDocument()
})
And my custom fetcher function:
async function serverFetch(endpoint) {
const response = await fetch("http://localhost:8080" + endpoint)
const body = response.json()
if (!response.ok) {
throw new Error("An error occurred while fetching the data.")
}
return body
}