Can't access object property even though should be there - javascript

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])

Related

React Prop returning Null as it relies on state

Hopefully a simply one.
I make an API call in my component which brings down some account information such as AccountUid, Category etc, i use state to set these.
useEffect(() => {
fetch(feed_url, {
headers: {
//Headers for avoiding CORS Error and Auth Token in a secure payload
"Access-Control-Allow-Origin": "*",
Authorization: process.env.REACT_APP_AUTH_TOKEN,
},
})
//Return JSON if the Response is recieved
.then((response) => {
if (response.ok) {
return response.json();
}
throw response;
})
//Set the Account Name state to the JSON data recieved
.then((accountDetails) => {
setAccountDetails(accountDetails);
console.log(accountDetails.accounts[0].accountUid);
console.log(accountDetails.accounts[0].defaultCategory);
})
//Log and Error Message if there is an issue in the Request
.catch((error) => {
console.error("Error fetching Transaction data: ", error);
});
}, [feed_url]);
This Works perfectly well and it Logs the correct values in my .then when testing it.
The issue however is that i want to pass these down as props. But i get an error that they are being returned as null (My default state).. i presume as they're jumping ahead.
<div className="App">
<GetAccountName
accountUID={accountDetails.accounts[0].accountUID}
defCategory={accountDetails.accounts[0].defaultCategory}
/>
</div>
How do i pass the the 2 details im logging as props?? I've tried setting default state to "" instead of null and just get that it is undefined.
If you dont want to use conditional render in your child component, so you should try optional chaining
<GetAccountName
accountUID={accountDetails?.accounts?.[0]?.accountUID}
defCategory={accountDetails?.accounts?.[0]?.defaultCategory}
/>
Since fetching is asyncronous, the most common way is to show some loading indicator (like a spinner) & once the data come in, show the component instead.
If you don't need an indicator, you might just return null.
The general idea is to manipulate some intermediary states (e.g. data, isError) based on the promise state.
Check out react-query library example or a lighter abstraction like useFetch hook to see how they manage it.
Here's a sample implementation of useFetch taken from this article:
const useFetch = (url, options) => {
const [response, setResponse] = React.useState(null);
const [error, setError] = React.useState(null);
const [abort, setAbort] = React.useState(() => {});
React.useEffect(() => {
const fetchData = async () => {
try {
const abortController = new AbortController();
const signal = abortController.signal;
setAbort(abortController.abort);
const res = await fetch(url, {...options, signal});
const json = await res.json();
setResponse(json);
} catch (error) {
setError(error);
}
};
fetchData();
return () => {
abort();
}
}, []);
return { response, error, abort };
};

Error: Error serializing `.data[4].description` returned from `getStaticProps` in "/" [duplicate]

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,
},
};
}

Data Not updated when fetch query - React-Query?

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.

React: Error : Objects are not valid as a react child?

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!

React, unable to display the data received by an api, after doing a GET in componentDidMount()

I have done a function in my react call which calls an api, and sets a state based on the data received by that api
getUserList() {
API.get('/userlist')
.then(response => {
this.setState({
userList: response.data
}, () => {
console.log(this.state.userList) //I can see this
});
})
}
This is how my constructor looks
constructor(props) {
super(props)
this.state = {
retracted: false,
userList: []
}
this.getUserList = this.getUserList.bind(this);
}
I call it in componentDidMount()
componentDidMount() {
this.getUserList();
}
But then my render crashes, because both console.log return undefined
render() {
var users = this.state.userList;
var user = users[0].email //cannot read email of undefined
console.log(this.state.userList)
console.log(users)
return (
<div class="maincontainer">
<div className="content-landing">
<p>{user}</p>
</div>
</div>
)
}
componentDidMount is not async, and cannot be. You should deal with transition state like loading during the async call.
Consider do a check since the result is not fetched yet :
render() {
var users = this.state.userList;
if (users.length === 0) {
return <p>loading</p>
}
return <p>{users[0].email}</p>
}
This could be not fully fonctionnal depending your usage, especially for error response. You still do a check for error case / state where users.length === 0 but not loading.
var user = users[0].email //cannot read email of undefined
this code is the culprit. because on the first render is empty.
can you try a null check before accessing users[0].email heres an example
var user = users.length > 0 ? users[0].email : ""
this is a case of initially undefined data, until there is data in users this line var user = users[0].email will crash because its trying to access non existing data so rewrite that part into:
var user = users && users[0] ? users[0].email : null
I have tried with Axios, please find below code snippet, as setting the state will result in render to execute:
var self = this;
axios.get('/url')
.then(function (response) {
console.log(response);
self.setState({userList: response.data})
})
.catch(function (error) {
console.log(error);
});

Categories

Resources