Access Array Inside Object - javascript

I have a javascript object and I want to map through one of its properties which is an array. But for some reason when I map through it or even call .length on the property which I know is an array it treats the property as if it's not an array.
Object is being created from an axios get request in an asynchronous object.
useEffect(() => {
async function fetchData() {
const dummyData = await axios.get(
'http://localhost:3000/data',
);
dummyData.data.videos.map(video => {
return (
setCurrentEdition(video.currentEdition[0])
);
});
}
fetchArtPiece();
return () => console.log('clean artPiece detail page');
}, []);
currentEdition: {
videoAssociated: {id: "oYu5J4TQbJ728c45ExWopZicY9LrCxNNTEZ3"}
bids: []
id: "Kr3fjeo51"
owners: (2) [{…}, {…}]
}
All im trying to do for now is .map() and it throws an undefined error
currentEdition.owners.map(owners => console.log(owners))
even when i check currentEdition.owners.length it throws an undefined error and when I log currentEdition.owners the proto: is an array and it has the square brackets and everything so why is this not being treated as an array when I try to operate on it?
Thank You

Well, since you are working with asynchronous data you should always make sure that you have your data and then start playing with it around.
So to fix this you need to make a condition for your JSX like this:
<Grid item xs={7}> {Array.isArray(currentEdition?.owners) && console.log(currentEdition.owners.length)} </Grid>

Related

How I can iterate a nested object to acces to their values in React Js

I've searched a lot for this and I don't get a suitable answer to solve this. I have an object that I've made from a fetched data and the structure is like this:
{
id: string,
title: string,
price: {
currency: string,
amount: number,
},
picture: string,
condition: string,
free_shipping: string,
sold_quantity: string,
};
When I try to iterate through the nested object (second level)
price: {
currency: string,
amount: number,
},
console throws an error to undefined. I've tried: Object.keys() method or transform the object into an array without success.
Does anyone know how I can iterate through this object and obtain the values of each property: currency, amount
Thanks in advance for your help.
To properly understand the issue, we do need to see exactly how you are trying to iterate your object. The short answer in how to do this, as mentioned by other users is
Object.keys(obj).map((key) => {
const value = obj[key]
// Do stuff with value (?)
return value
})
Of course, for the example above, we will not properly access the keys of your nested object (price), for that to happen you'd have to do an Object.keys(obj).map(()=>{}) within the one you already have. So something like this:
Object.keys(obj).map((key) => {
const value = obj[key]
// Do stuff with value (?)
if (typeof value === 'object') {
const nestedObjectKeys = Object.keys(value).map((nestedKey) => {
// Do stuff
})
}
return value
})
})
This is where we do need more details about your question, if you know that that is what you want to access and iterate through, just use the same thing, but directly on the price object. Should look like this:
Object.keys(obj.price).map((key) => {
const priceValue = obj.price[key]
// Do stuff with priceValue (?)
return priceValue
})
Here is a simple demo with both cases:
const App = () => {
const obj = {
id: 'my_id',
title: 'my_title',
price: {
currency: 'USD',
amount: 100,
},
picture: 'my_picture',
condition: 'my_conditon',
free_shipping: 'free_shipping',
sold_quantity: 'sold_quantity',
};
return (
<div>
<h3>Example 1: Iterating through the whole object</h3>
{Object.keys(obj).map((key) => {
const value = obj[key]
return (
<div key={key}>
{typeof value === 'object'
? (
<div>
<hr/>
<h4>Found a nested object! Lets now do another Object.keys().map!</h4>
{Object.keys(value).map((nestedKey) => (
<div key={nestedKey}>{nestedKey}: {value[nestedKey]}</div>
))}
<hr/>
</div>
)
: <span>{key}: {value}</span>
}
</div>
)
})}
<h3>Example 2: Simply iterating only through the nested object</h3>
{Object.keys(obj.price).map((key) => (
<div key={key}>
{key}: {obj.price[key]}
</div>
))}
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Edit: as Drew Reese pointed out, in this case there is actually no reason to iterate through the keys instead of the values (Object.keys(obj) vs Object.values(obj)). Unless you for some reason need access to the keys. everything described above could be done in a more cleaner fashion with Object.values(obj). So the examples may then become:
Object.values(obj).map((value) => {
// Do stuff with value (?)
return value
})
and
Object.values(obj.price).map((value) => {
// Do stuff with value (?)
return value
})
First of all, I'm really appreciate the answers to my question. It was really helpful!
In fact, my mistake was from the state that I've defined inside my code. I'll try to explain it in the next lines.
This object structure (below) was made for me to receive the data from an API:
{
id: string,
title: string,
price: {
currency: string,
amount: number,
},
picture: string,
condition: string,
free_shipping: string,
sold_quantity: string,
};
The API doesn't maintain the same structure that I show you below, so I have to resample in the promise on the fetch. But when I made the resample, I didn't insert the price object.
So, when I try to use Obj.map. React explodes and said to me that it cannot iterate on an undefined.
That was really a headache because I was trying to understand why I cannot iterate over a nested object. But, in the end, that was not the base issue.
And finally, the way to integrate the data fetched from the API and later transformed to my desire structure was more simple than I thought.
Just with the dot notation, I could access the specific item that I've need from the object and object nested. for example:
item.price.amount // Returns 1990
item.price.currency // Returns USD
Hope this answer could help you. But this issue makes me understand fully deep how to iterate objects through Objects.keys(obj).map(key =>(//code))
Especial thanks to #theJuls for the detailed explanation.
have you tried this method?
Object.keys(myObject).map(function(key) {
});

Object items are not rendering (Object is stored in an array of objects which in turn is stored in a React state hook)

My problem is that item stored in object (in an array of objects inside a state hook) is not being rendered on page, but it gets printed with console.log
I fetched some data from the server and it worked as expected, returning an array of two items, one of which is an object containing blog data(blog heading, creator, etc) an another is an array of 'sections' of that blog. Here is how I did it,
This is the initialization
// Get the blog id
const {blog_id} = useParams();
// Declaring our state hooks
const initial_blog_state = {
blog_id: blog_id,
heading: '',
creator: {},
created: '',
section: [],
}
const [blog_state, updateBlogState] = useState(initial_blog_state);
Here is the fetching of data from the server
useEffect(() => {
// Fetching data
Promise.all([
fetch(`http://127.0.0.1:8000/api/blog/${blog_id}`),
fetch(`http://127.0.0.1:8000/api/section/full/${blog_id}`)
]).then(responses => {
// Get a JSON object from each of the responses
return Promise.all(responses.map(response => {
return response.json()
}))
}).then(function (data) {
// Log the data to the console
console.log(data);
// Update state
updateBlogState({
...blog_state,
heading: data[0].heading,
creator: data[0].creator,
created: data[0].created,
section: data[1]
})
}).catch(function (error) {
// if there's an error, log it
console.log(error);
});
}, []);
I think the way I'm updating the section inside the hook can be a problem(although I'm not sure how), because I saw in a stackoverflow answer that objects must always be initialized (which I'm not doing when declaring an array of 'objects')
And here is the thing that needs to be rendered
return (
<div className="container">
<h1>{blog_state.heading}</h1>
<p className="text-small text-muted">{blog_state.creator.username}</p>
{blog_state.section.map(item => {
{console.log(item.paragraph)}
<p>{item.paragraph}</p>
})}
</div>
)
Here blog_state.heaing and blog_state.creator.username are being rendered as desired and also console.log(item.paragraph) prints the correct paragraph on the console window, but item.paragraph doesn't show anything on the page.
Nothing is being returned from your map.
i.e you need to add a return line before the <p>{item.paragraph}</p>
So:
{blog_state.section.map(item => {
console.log(item.paragraph)
return <p>{item.paragraph}</p>
})}
or as an inline return:
{blog_state.section.map(item => <p>{item.paragraph}</p>)}

Array length is zero but Array is not empty

When I'm trying to iterate through an array, get it's length or access indexes I'm getting Error TypeError: Cannot read property 'map' of undefined.
The array isn't empty and when I console.log() it I've gotten.
0: {user_id: 11, …}
length: 1
__proto__: Array(0)
I see that proto: Array(0) and I'm assuming this means it's a 0 length Array but how do I make it non-zero length so that I can iterate through it?
Code for reference:
useEffect(() => {
blog.authors.map(data => {
console.log(data)
})
}, [blog])
I've also tried. It worked, but I immediately got the similar error.
useEffect(() => {
(async() => {
await blog.authors.map(data => {
console.log(data)
})
})()
}, [blog])
The simple checking if blog.authors isn't undefined solved it.
No, array indexes are 0 based.
var arr = ['a', 'b'];
console.log(arr.length); // => 2
console.log(arr[0]); // => a
So instead of making confusing assertion about what works and not without providing the code them providing other code, could you just see the result of that:
useEffect(() => {
console.log({blog});
console.log({authors: blog.authors });
blog.authors.map(data => {
console.log(data)
})
}, [blog])
Because I suspect that blog changed overtime, having some authors and something authors undefined.

fetch gives me a full array but it is empty when i try to access it

I get an object from a Symfony rest API. This object has a property "shooting" which is an array and this array is full when I console.log it but when i try to access it, it is empty
This is my fetch request
const getProjectsAvailable = async () => {
const data = await fetch(
`${process.env.GATSBY_CORE_URI}/api/dashboard/supplier/projects/available`,
{
headers: {
[`X-Auth-Token`]: `${token}`,
[`Accept`]: `application/json`,
},
}
);
return data;
};
Here is the project object that i get back from fetch request
0: {id: 258, name: "Project26-1", reference: "A6568", isOfferValidated: null, source: "dashboard", …}
It has a shooting key which contains an array and it is not empty
shootings: Array(1)
0:
addressCity: "Paris"
addressCountry: {id: 76}
But when i set this object to my component state, all values stay the same except the shooting key which becomes an empty array
const [projects, setProjects] = useState([]);
useEffect(() => {
getProjectsAvailable().then(res =>
res.json().then(data => {
setProjects(data);
})
);
}, []);
I have no idea why does it act like that.
Thanks in advance
EDIT :
For example, the first line with console.log gives me the response object with a full shooting array while the second one sets it to my state but shooting array is empty
useEffect(() => {
getProjectsAvailable().then(response => console.log(response));
getProjectsAvailable().then(response => setProjects(response));
}, []);
Ok it is my bad. Somewhere else in the code, there was a .split() on the shooting property which mutates the array so the props changed and shooting array got empty

Problem mapping an array received from nodejs in React

I'm trying to use an array returned from Node in a React code.
Here's the Node's router code:
router.get('/prodlist', async (req, res) => {
try {
await Product.find({}).exec((err, result) => {
res.status(200).send({ express: result })
})
} catch (e) {
console.log(e)
res.status(400).send(e)
}
})
The React's component code:
import React, { useState, useEffect } from 'react';
function App() {
const [datas, setDatas] = useState(null)
useEffect(() => {
var curURL = window.location.href
var calledRoute = curURL.substr(8).substr(curURL.substr(8).indexOf("/"))
callBackendAPI(calledRoute)
.then(res => setDatas(prevData => res.express))
.catch(err => console.log(err));
}, [])
async function callBackendAPI(routePath) {
const response = await fetch(routePath);
const body = await response.json();
if (response.status !== 200) {
throw Error(body.message)
}
console.log(body.express)
return body;
}
return (
<div className="App">
{{datas}}
</div>
)
}
export default App;
And the array content (seen from Chrome's developer tools):
(3) [{…}, {…}, {…}]
0:
prodDesc: "Prod1"
prodID: "1"
prodImage: "prod1.jpg"
prodPrice: 350
prodRebate: 100
prodReviews: []
prodStock: 31
prodThumb: "prod1.jpg"
__v: 0
_id: "5eb04a6439eec26af4981541"
__proto__: Object
1: {prodID: "2", prodDesc: "Prod2", prodThumb: "prod2.jpg", prodImage: "prod2.jpg", prodPrice: 1500.79, …}
2: {prodID: "3", prodDesc: "Prod3", prodThumb: "prod3.jpg", prodImage: "prod3.jpg", prodPrice: 280.79, …}
length: 3
__proto__: Array(0)
When I execute this code I get the error: Objects are not valid as a React child
So I tried to map data's content with a line like this:
{datas.map((item) => <p>{item}</p>)}
Executing this code with a map I get the error : Cannot read property 'map' of null.
Although datas contains an array, it seems React sees it as null.
Anybody understands this issue?
EDIT: I didn't realized that this question is about two different issues. Norbitrial's answer fixed the problem about synchronicity but I still get the error "Objects are not valid as a React child [...] use an array instead".
EDIT2: Solved the issue by forwarding the mapping to a REACT component with the following line:
{datas && datas.map(item => <ShopCart key={item.prodID} item={item} />)}
The problem is your backend API returns the result asynchronously so it might take few seconds while your state value is null.
The solution can be checking for null value first then using the .map() on your state called datas with && in your code.
Try as the following:
{datas && datas.map((item) => <p>{item}</p>)}
I hope this helps!
The code runs before datas is populated from your api, you need to first check to make sure it is populated like this.
{datas && datas.length && datas.map((item) => <p>{item}</p>)}
The problem is that your component gets mounted before you receive the data.
What I would do(and most people) is to create a loading component that will show a spinner or something like that when the array is empty and after it gets filled it should render the component with the data.
But a quick solution to this is to do a conditional render
{datas.length && datas.map((item) => <p>{item}</p>)}

Categories

Resources