Reputation: 251
I am sending data from Node JS to React JS in array object. In React JS when I am setting response data I am getting error "Objects are not valid as a React child (found: object with keys {eventName, eventDate, merchantid}). If you meant to render a collection of children, use an array instead."
I checked one of the Stackoverflow post useState Array of Objects. I am also setting value same way, but I am getting error.
Below data I am sending from Node JS.
[
{
eventName: 'Sankranti',
eventDate: 2021-01-21T00:00:00.000Z,
merchantid: 'tp012345'
},
{
eventName: 'Sankranti 1',
eventDate: 2021-01-26T00:00:00.000Z,
merchantid: 'tp012345'
}
]
Below screen shot I can see error and response data on the console.
Below my code, I am getting error at setEventList(eventList => [...eventList, response]). Based on comments I added below code.
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import Carousel from 'react-bootstrap/Carousel'
import axios from 'axios';
import DashboardNavBar from './DashboardNavBar';
import Header from './Header';
const DashboardPage = (props) => {
const [eventList, setEventList] = useState([])
const [index, setIndex] = useState()
if (!props.profileData) {
useEffect(() => {
(async () => {
const eventsList = await axios.get(
"http://localhost:3000/api/dashboard"
);
console.log(eventsList.data)
const response = eventsList.data
setEventList(eventList => [...eventList, response])
if(!response){
setErrorMsg('Please create Event and then add User !!')
}
})();
}, []);
}
const eventListRender = eventList.length > 0 ?
eventList.map((item,index) => {
console.log('item name: ', item[index].eventName)
return <Carousel.Item>{item[index].eventName}</Carousel.Item>
}) :
<Carousel.Item>No upcoming events</Carousel.Item>
const handleSelect = (selectedIndex) => {
console.log(eventList)
console.log(selectedIndex)
setIndex(selectedIndex)
}
return (
<div>
<DashboardNavBar />
<Header />
<p >Welcome !!!!!!</p>
<Carousel
activeIndex={index}
onSelect={handleSelect}
>
{eventListRender}
</Carousel>
</div>
);
}
const mapStateToProps = (state) => ({
profileData: state.auth.profileData
})
export default connect(mapStateToProps) (DashboardPage);
After adding below code it always reading first occurrence
const eventListRender = eventList.length > 0 ?
eventList.map((item,index) => {
console.log('item name: ', item[index].eventName)
return <Carousel.Item>{item[index].eventName}</Carousel.Item>
}) :
<Carousel.Item>No upcoming events</Carousel.Item>
Please find the updated results
Upvotes: 1
Views: 169
Reputation: 202836
Ok, your codesandbox confirms what I suspected. In your sandbox you've directly placed that mock response in your state as a flat array
const [eventList, setEventList] = useState([
{
eventName: "Sankranti",
eventDate: "2021-01-21T00:00:00.000Z",
merchantid: "tp012345"
},
{
eventName: "Sankranti 1",
eventDate: "2021-01-26T00:00:00.000Z",
merchantid: "tp012345"
}
]);
This allows the render to work as you expected, simply mapping over this flat array of objects.
eventList.map((item, index) => {
return <Carousel.Item>{item.eventName}</Carousel.Item>;
})
But in your original code you are not updating your state to be a flat array. The response
is an array, i.e. [object1, object2]
and you append this array to the end of your state's eventList
array.
setEventList(eventList => [...eventList, response])
This updates your state to something like this [[object1, object2]]
, so the mapping function you used only maps one element.
eventList.map((item, index) => {
return <Carousel.Item>{item[index].eventName}</Carousel.Item>;
})
The reason is because you used the array index of the outer (recall eventList
is an array of length 1) to access into the inner nested array (array of length 2). In iterating the outer array the index
only reaches value 0
, so only the zeroth element of the inner array is rendered.
See a more accurate reproduction of your issue in this code:
const response = [
{
eventName: "Sankranti",
eventDate: "2021-01-21T00:00:00.000Z",
merchantid: "tp012345"
},
{
eventName: "Sankranti 1",
eventDate: "2021-01-26T00:00:00.000Z",
merchantid: "tp012345"
}
];
function App() {
const [eventList, setEventList] = useState([]);
useEffect(() => {
setEventList((eventList) => [...eventList, response]);
}, []);
const eventListRender =
eventList.length > 0 ? (
eventList.map((item, index) => {
return <Carousel.Item>{item[index].eventName}</Carousel.Item>;
})
) : (
<Carousel.Item>No upcoming events</Carousel.Item>
);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Carousel>{eventListRender}</Carousel>
</div>
);
}
If the response data is also an array then it seems you should spread it into your eventList
state array so it remains a nice, flat array.
Additionally, as pointed out by @Ashish, your useEffect
hook usage is invalid and breaks the rules of hooks by being placed in a conditional block. The effect needs to be in the body of the component, so the condition should be tested in the effect callback. Refactor the anonymous async
function to be a standard named function, and invoke in a conditional check within the effect callback.
useEffect(() => {
const getEvents = async () => {
const eventsList = await axios.get("http://localhost:3000/api/dashboard");
console.log(eventsList.data);
const response = eventsList.data;
setEventList((eventList) => [
...eventList, // <-- copy previous state
...response, // <-- spread array response
]);
if (!response) {
setErrorMsg("Please create Event and then add User !!");
}
};
if (!props.profileData) { // <-- check condition for fetching data
getEvents();
}
}, []);
const eventListRender =
eventList.length > 0 ? (
eventList.map((item, index) => {
return <Carousel.Item key={index}>{item.eventName}</Carousel.Item>;
})
) : (
<Carousel.Item>No upcoming events</Carousel.Item>
);
Demo with mocked axios data fetch.
Upvotes: 1