Objects are not valid as a React child - found: object with keys - reactjs

I try to setState but it does not accept the JSON responses I fetch from a URL. but the response looks ok, it is an array of objects:
{"songs":
[
{"id":1,"name":"Hello","singer":"Adele","img":"adele.png","type":"pop","mp3":"Adele.mp3"},
{"id":2,"name":"de una vez","singer":"Selena gomez","img":"selena.png","type":"pop","mp3":"Selena.mp3"},
{"id":3,"name":"Bayda","singer":"Navid","img":"navid.png","type":"pop","mp3":"Navid.mp3"},
{"id":4,"name":"Takin' Back My Love ","singer":"Enrique Iglesias","img":"enrique.png","type":"Pop","mp3":"Enrique.mp3"}
]
}
and here is my react component:
import React, {useState, useEffect} from "react";
const App = () => {
const [songs, setSongs] = useState([]);
const [playing, setPlaying] = useState({});
useEffect(() => {
fetch(`http://localhost:8765`)
.then(res => res.json())
.then(jsonRes => {
// setPlaying(jsonRes[0]);
setSongs(jsonRes.songs);
// jsonRes.songs.map(song => setSongs([...song]))
console.log('Songs are: ', jsonRes.songs);
});
}, []);
// console.log('songs:', songs)
// console.log('playing:', playing)
return (
<div>
Hello from App Songs: {songs}
</div>
);
}
export default App;

You should use map to display an array in React.
Read more: https://reactjs.org/docs/lists-and-keys.html

This songs array is an array of objects and you cannont pass it to the return statement. If you want to have the content of if you need to do as below in your return statement, for example you want to show the name of each song:
{songs.map(song => {
return (
<div>{song.name}</div>
)
})}

Related

useEffect and useState to fetch API data

I want to use useEffect(on mount) to fetch from API and store it in useState. Fetch API is used to get the data. The problem is when initial page loading and also when I reload the page, it outputs an error called test.map is not a function. Why this happening and how to avoid this ?
import { useEffect, useState } from 'react';
function App() {
const[test, setTest] = useState({})
useEffect(() => {
testfunc()
}, [])
async function testfunc(){
let api = await fetch('https://jsonplaceholder.typicode.com/users')
let apijson = await api.json()
setTest(apijson)
}
return (
<div className="App">
{
test.map((item) => {
return(
<div>
{item.name}
</div>
)
})
}
</div>
);
}
export default App;
You can't map on an object {}, so you should need to define an array [] for the base state :
const[test, setTest] = useState([])
You have to change {} to array first to be able to map over it. You can easily place ? after test like this. or make in the default value of the state a default value for item name. because this error results as you map over an empty object.
import { useEffect, useState } from 'react';
function App() {
const[test, setTest] = useState([{name:"default"}])
useEffect(() => {
testfunc()
}, [])
async function testfunc(){
let api = await fetch('https://jsonplaceholder.typicode.com/users')
let apijson = await api.json()
setTest(apijson)
}
return (
<div className="App">
{
test?.map((item) => {
return(
<div>
{item.name}
</div>
)
})
}
</div>
);
}
export default App;
As already mentioned, you can't use the .map for objects.
Instead of this, you can make something like that
Object.keys(test).map(key => {
const currentSmth = test[key]
return(
<div>
{currentSmth.name}
</div>
)
})
})
I think it helps you to solve your problem.
Be careful using the correct data structures and methods.

Rendering nested json response data in react UI

This is the structure of the json being fetched. I am trying to render some of the nested threads data to a web page with react.
import react, {useState, useEffect} from "react";
import axios from 'axios'
import ReactJson from 'react-json-view'
const FeaturedBoards = () => {
const [boards, setBoards] = useState([{page: '', threads: {}}]);
useEffect(() => {
fetchBoards();
}, []);
const fetchBoards = () => {
axios.get('https://a.4cdn.org/po/catalog.json')
.then((res) => {
console.log(res.data);
setBoards(res.data);
})
.catch((err) => {
console.log(err);
});
};
if(boards === 0) {
return <div>Loading...</div>;
}
else{
return (
<div>
<h1>Featured Boards</h1>
<div className='item-container'>
{boards.map((board) => (
<div className='board' key={board.id}>
<p>{board['threads']}</p>
</div>
))}
</div>
</div>
);
}
};
export default FeaturedBoards;
I have tried everything to display some of the nested threads data but nothing comes up. I've tried doing a second call to map on board but no luck, storing it in a variable and calling from that still nothing. Am I doing something totally wrong?
I believe this is more fully answered by How can I access and process nested objects, arrays or JSON?. but to explain for this particular data structure, keep reading.
Look at your actual data... boards is an array. Each element in it is an object with page (int) and threads (array) properties. Each threads array element is an object with other properties. You can use map to iterate arrays and return a JSX representation of the objects within.
For example
const [boards, setBoards] = useState([]); // start with an empty array
const [loading, setLoading] = useState(true)
useEffect(() => {
fetchBoards().then(() => setLoading(false))
}, []);
const fetchBoards = async () => {
const { data } = await axios.get('https://a.4cdn.org/po/catalog.json')
setBoards(data)
}
return loading ? <div>Loading...</div> : (
<div>
<h1>Featured Boards</h1>
<div className="item-container">
{boards.map(board => (
<div className="board" key={board.page}> <!-- 👈 note "page", not "id" -->
{board.threads.map(thread => (
<p>{thread.name}</p>
<p>{thread.sub}</p>
<p>{thread.com}</p>
<!-- etc -->
))}
</div>
))}
</div>
</div>
)

How to properly iterate or loop through an objects in react

The code below queries a record from an Atlassian Storage API.
with console.log(data) displays the records as objects objects.
with console.log(data.first_name) and console.log(data.last_name), I can successfully see the name Lucy and Carrots in the console.
Here is my Issue:
When I try to loop through the objects in other to display the records as per code below. it displays Error
TypeError: Cannot read property 'length' of undefined
at Object.App
If I remove the projects.length and try to display records, it will show error
TypeError: Cannot read property 'map' of undefined
Below is my effort so far
import api, { route } from "#forge/api";
import ForgeUI, { render, Fragment, Text, IssuePanel, useProductContext, useState, Component, useEffect} from "#forge/ui";
import { storage} from '#forge/api';
const fetchData = async () => {
//const data = {first_name: 'Lucy', last_name: 'Carrots' };
const data = await storage.get('key1');
console.log(data);
console.log(data.first_name);
console.log(data.last_name);
};
const App = () => {
const [ projects ] = useState(fetchData);
fetchData();
return (
<Fragment>
<Text> Display Objects Records</Text>
{projects.length ? projects.map((i, v) => (
<Text key={v}>
<Text>
<Text>First Name: {v.first_name}</Text>
<Text>Last Name: {v.last_name}</Text>
</Text></Text>
)): <Text>No data stored yet...</Text>}
</Fragment>
);
};
export const run = render(
<IssuePanel>
<App />
</IssuePanel>
);
I see the response data return an object, so you don't need to use array with map.
And you should call API in the useEffect.
const App = () => {
const [projects, setProjects] = useState(null);
useEffect(() => {
const fetchData = async () => {
const data = await storage.get("key1");
setProjects(data);
};
fetchData();
}, []);
...
{
projects ? (
<Text key={v}>
<Text>
<Text>First Name: {projects.first_name}</Text>
<Text>Last Name: {projects.last_name}</Text>
</Text>
</Text>
) : (
<Text>No data stored yet...</Text>
);
}
...
}
Try const [projects, setProjects] = useState([]) instead of const [projects] = useState(fetchData). This will make your state start out as an empty array that the API call can exchange for a filled one later by using setProjects().
Then, inside fetchProjects, you can call setProjects(data) to update your state, which will rerender your page.
Finally, calling fetchData() in the place you do now will cause it to be called every time your App is rendered, which is inefficient.
Instead, call it inside a useEffect() hook:
useEffect(fetchData, []);
This will call fetchData() once when the page is loaded.

How to map an array of maps from firestore in react

I'm trying to navigate an array of orders stored in each "User". I am able to query and find ones that have orders but I'm not able to display them. I keep getting an error "Cannot read property 'map' of null". Where am I going wrong?
The image below shows how all the orders are stored in "order"
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { firestore } from "../../../FireBase/FireBase";
const OrdersAdmin = (props) => {
const [order, setOrder] = useState(null);
useEffect(() => {
const fetchOrder = async () => {
const doc = await firestore.collection("Users");
const snapshot = await doc.where("orders", "!=", []).get();
if (snapshot.empty) {
console.log("No matching documents.");
return <h1>No Orders</h1>;
}
var ans = [];
snapshot.forEach((doc) => {
console.log(doc.id, "=>", doc.data().orders);
setOrder(doc.data().orders)
});
};
fetchOrder();
}, [props]);
return (
<div className="adminOrders">
<h1>orders</h1>
{console.log(order)}
{order.map((orderItem) => (
<div className="singleOrder" key={orderItem.id}>
<p>{orderItem}</p>
</div>
))}
</div>
);
};
export default OrdersAdmin;
The issue is that the initial value of order is null. null does not have Array.prototype.map, therefore you get the error. Try updating your render to use conditional rendering to only attempt Array.prototype.map when order is truthy and an Array:
{order && order.length > 0 && order.map((orderItem) => (
<div className="singleOrder" key={orderItem.id}>
<p>{orderItem}</p>
</div>
))}
Otherwise you can use a better default value of an empty array for order which would have Array.prototype.map available to execute:
const [order, setOrder] = useState([]);
Hopefully that helps!

React js giving error Objects are not valid as a React child, I used hooks

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
Issue
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>
);
}
Solution
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.

Resources