How to access the Array field in the Json Object? [duplicate] - javascript

This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed last month.
So I have a Json object FriendJson and in it I have a array field friends.
Json Object:
[
{
"id": 4,
"updated": "2023-01-07T22:06:23.929206Z",
"created": "2023-01-07T19:49:49.303182Z",
"user": 35,
"friends": [
36,
37,
38,
39
]
}
]
The question is how to access friends array and traverse it. I am using react so I need to use map() for traversal. I am trying this in return part of a Functional component, so can use much functionalities of Javascript.
My component:
import React, {useContext, useEffect, useRef, useState} from 'react'
import { useNavigate } from 'react-router-dom';
import AlertContext from '../context/Alerts/AlertContext';
import FriendContext from '../context/Friends/FriendContext';
import FriendItem from './FriendItem'
export default function YourFriends() {
const {friendJson, getFriends, addFriend, getUserId, getReceiverId} = useContext(FriendContext)
const {showAlert} = useContext(AlertContext)
const navigate = useNavigate()
useEffect (() => {
if(localStorage.getItem('authToken')) {
getFriends()
}
else navigate('/signin')
})
return (
<>
<div className="container">
<h2 className="text-center">Your Friends</h2>
{
friendJson.length === 0 && <div className="text-conter">You don't have any friends</div>
}
{
// console.log(friendJson.friends)
friendJson.friends.map((eachFriend) => {
return <FriendItem key={eachFriend} friend={eachFriend}/>
})
}
</div>
</>
)
}
I tried in this way:
friendJson.friends.map((eachFriend) => {
return <FriendItem key={eachFriend} friend={eachFriend}/>
})
But it throws error as:
TypeError: Cannot read properties of undefined (reading 'map')
And when I console.log(FriendJons.friends) the result is undefined.

If your FriendJson is an array you should go to friends by
FriendJson[0].friends
rather than
FriendJson.friends
potentially you can iterate ofer FriendJson and in any of his elements get friends.
friendJson.map((element) => {
return element.friends.map((eachFriend) => {
return <FriendItem key={eachFriend} friend={eachFriend}/>
})
})
general rule for errors like "Cannot read properties of undefined (reading 'map')"
is print object having property on which you need to read "map" in your case if this solution will not work then check what is friendJson.

Related

Why my code is not mapping through the array of object in react using id as the key?

I am able to print the blog array in the console and it includes the object. I want to use the object components by mapping through the id as the key but I am not able to get inside the map function. I was able to use the same map structure in my another component of my app but this one is not working. I tried map function in different ways but I am not able to get this one fixed. Can I get some review and advise on my code?
import {colRef} from "./firebase";
import { useParams } from "react-router-dom";
import {doc, onSnapshot} from 'firebase/firestore'
import { useEffect, useState } from "react";
const BlogDetails = () => {
const{id}=useParams();
const[blog,setBlog] = useState(null);
///Getting single document.
useEffect(() => {
const docref= doc(colRef,id);
let document =[];
onSnapshot(docref,(doc) => {
document.push({...doc.data(),id:doc.id})
})
///console.log(document)
if(document)
{
setBlog(document);
}
},[id]);
return (
<div className="blog-details">
{blog && (console.log(blog))}
{blog && (blog.map((blog) => (
<div className="blog-info" key={blog.id}>
{console.log('Inside blog-info')}
{console.log(blog)}
<h2>{blog.title}</h2>
<p>Written by {blog.author}</p>
<div>{blog.body}</div>
<button>delete</button>
</div>
)))}
</div>
);
}
export default BlogDetails;
This code is asynchronouse and it can't work like this:
const docref= doc(colRef,id);
let document =[];
onSnapshot(docref,(doc) => {
document.push({...doc.data(),id:doc.id}) // than this
})
if(document)
{
setBlog(document); // this runs first
}
It should look more like that:
const docref= doc(colRef,id);
onSnapshot(docref,(doc) => {
setBlog({...doc.data(),id:doc.id}))
})

React State Changing Without Explicitly Telling it to Change [duplicate]

This question already has answers here:
Updating an object with setState in React
(23 answers)
Why can't I directly modify a component's state, really?
(7 answers)
How to update nested state properties in React
(36 answers)
Closed 12 months ago.
My react state is changing without me calling my setState function from useState hook.
After some quick research, i've narrowed it down to the fact an array is a reference type so data and tempData share the same reference and change with each other. A solution I found was to stringify then parse the data: JSON.parse(JSON.stringify(data)) but this could have some pretty bad performance hits if it's a large object right? Is there a better solution here? Redux? Or is that unnecessary? This is a pretty common case isn't it?
For anyone who cares, this works too but is kinda ugly:
change state to object rather than array
const defaultData = {
data: [
{id:0, foo:1, bar:2},
{id:1, foo:3, bar:4},
{id:2, foo:4, bar:6},
]
}
const handleData = (id) => {
setData((prevState) => {
return {data: data.data.map((i) => i.id === id ? {...i, id:i.id+10} : {...i})}
})
}
I've attached an example below which can be easily created from create-react-app.
App.js
import Child from './Child';
import { useState } from 'react';
const defaultData = [
{id:0, foo:1, bar:2},
{id:1, foo:3, bar:4},
{id:2, foo:4, bar:6},
]
function App() {
const [data, setData] = useState(defaultData)
const handleData = (id) => {
const tempData = data
for (const idx in tempData) {
const item = tempData[idx]
if (item.id === id) {
tempData[idx].id += 10
}
}
}
return (
<div>
<Child data={data} handleData={handleData} />
</div>
);
}
export default App;
Child.js
export default function Child(props) {
const {data, handleData} = props
return (
<div>
<ul>
{data.map((i) => (
<li key={i.id}>
<button onClick={() => handleData(i.id)}>
{i.foo} {i.bar}
</button>
</li>
))}
</ul>
</div>
)
}

Object destructuring in map function

Below is the component I am working on:
//PURPOSE : Component to show some useful information about the current page. like LAST REFRESH TIME OF DATA.
//Props that needs to be passed. - {MessageTitle}
//
import React, { useEffect, useState } from "react";
import "../../css/BubbleInfoComponent.scss";
import ApiHelper from "../../api/ApiHelper";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
const BubbleInfoComponent = (props) => {
let [Info, setInfo] = useState({});
function onClicko() {
console.log("Handling Click");
}
useEffect(() => {
getData();
// eslint-disable-next-line
}, [Info]);
function getData() {
ApiHelper.GetLastRefreshTime(props.uid).then((response) => {
// const infoData = response.data.map(({sysName, lastRefreshTime}) =>{
// return (sysName ? <div key={sysName}>`{sysName}:{lastRefreshTime}`</div>): <div>{lastRefreshTime}</div>;
// })
// setInfo(infoData);
// console.log("infoData:- ")
// console.log(infoData);
});
}
return (
<>
<div id="Bubble__Circle" onClick={onClicko}>
<p>
<FontAwesomeIcon icon="info" color="#30343f" />
</p>
</div>
<div id="Bubble__Content">
<div id="Bubble__Content_Msg_Title">{props.MessageTitle}</div>
{/* <div id="Bubble__Content_Msg_Title">{Info}</div> */}
</div>
</>
);
};
export default BubbleInfoComponent;
There will be two kind of JSON response I will be getting:
{
"SystemA": "07-04-2021 08:00",
"SystemB": "07-04-2021 08:00",
"SystemC": "07-04-2021 08:00"
}
{"responses": "07-04-2021 08:00"}
What I want to implement is 1st type of response I want to set the value of Info in "Key": "Value" format and for 2nd type of response only time should be visible.
I hope I made my point clear. I know I did something wrong while destructuring in line21, this might look silly but being a newbie to JavaScript, I am not able to identify where i am going wrong.
Map function can be run on arrays and not objects. So the first thing which has to be done is to convert object into and array. For that you can use the Object.entries which will give you the key and values for each of the elements in the object.
Reference - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
Based on the response type you either access the time directly or run through a map. Here is the code to start with :
ApiHelper.GetLastRefreshTime(props.uid).then((response) => {
const mapSystemNames = (data) => Object.entries(data).map(([key,value]) => <div key={key}>{key}:{value}</div>)
const infoData = response.data.responses? <div>{response.data.responses}</div> : <div>{mapSystemNames(response.data)}</div>;
// setInfo(infoData);
// console.log("infoData:- ")
// console.log(infoData);
});
Probably there may be some edge case scenarios / type checks needed to make it more robust, but this could get you started in the right direction. Please let me know if you have any queries.

React Hooks - useState initializes empty array that becomes object?

In the following code, I believe I am initializing gameList as an empty array. The first console.log shows gameList is an empty array. I then use a console.log in useEffect() that displays gameList as an object but I do not believe that I am doing anything to transform gameList. Can anyone explain this? I was trying to pass an array of objects to a child component but even upon using Object.values() on the gameList "object" it is still returning as an object. Thanks!
Edit: Perhaps the way I should've has asked this is: "Why does gameList show up in child component as an object with gameList as property? Why does it not arrive in the GameList component as an empty array called gameList? This is happening before submitting my form by the way.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import GameList from './GameList';
const Searchbar = () => {
const [searchString, setSearchString] = useState('');
const [gameList, setGameList] = useState([]);
console.log(gameList); // []
useEffect(() => {
console.log('gameList is ' + typeof gameList); // gameList is object
});
const requestGames = searchString => {
axios
.get(`http://localhost:3001/game/${searchString}`)
.then(({ data }) => setGameList(data))
.catch(e => console.log(e));
};
return (
<div>
<form
onSubmit={e => {
e.preventDefault();
requestGames(searchString);
}}
>
<label htmlFor="search-string">Search</label>
<input
type="text"
placeholder="search.."
onChange={e => setSearchString(e.target.value)}
/>
</form>
<GameList gameList={gameList} />
</div>
);
};
export default Searchbar;
Arrays have type object in JavaScript:
console.log(typeof []) //=> "object"
You can read more about the typeof operator on MDN [1].
To check if something is an array you can do this:
console.log(Array.isArray([])) //=> true
Or this:
console.log(Object.prototype.toString.call([])) //=> "[object Array]"
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof

I cannot get into nested JSON object, but only sometimes

I am trying to fetch data from an API (the Magic the Gathring Scryfall API) that has a nested object while using ReactJS. As soon as I try to use data from a nested object, I get a "cannot read png of undefined". I figured out this was probably an async problem, and fixed it by changing the state of my initial array to null, then adding an if statement to the render, but as soon as I changed the API url from https://api.scryfall.com/cards?page=3 to https://api.scryfall.com/cards/search?order=cmc&q=c%3Ared+pow%3D3, I can no longer access the image urls in the nested object again, despite having a JSON in the same format returned to me as the first URL. I'm just at a loss now.
I tried using axios, and I tried putting the fetch into a separate function, then putting that function into componentDidMount, but no luck. Setting 'cards' to null and then putting the "if (cards === null) { return null; } into the render worked for the first link, but not the second.
import React,{Component} from 'react';
import logo from './logo.svg';
import './App.css';
import Login from './components/Login'
class App extends Component {
constructor() {
super()
this.state = {
cards: null,
isUpdated:false
}
}
componentDidMount() {
this.populateCards()
}
populateCards() {
let url = 'https://api.scryfall.com/cards/search?order=cmc&q=c%3Ared+pow%3D3 '
fetch(url)
.then(response => response.json())
.then(json => {
console.log("setting the state.")
console.log(json.data)
this.setState({cards: json.data})
})
}
render() {
const { cards } = this.state;
if (cards === null) {
return null;
}
let cards1 = this.state.cards
let cardItems = cards1.map((card) => {
return (
<li>{card.name} - {card.id}
<p></p><img src={card.image_uris.png}/></li>
)
})
return (
<div className="App">
<h1>HOME PAGE</h1>
<Login />
<ul>
{cardItems}
</ul>
</div>
)
}
}
export default App;
Just need to figure out what is going on with this JSON before I can move on to writing up some search boxes. Greatly appreciate any help that can be offered.
The JSON coming back looks like so:
{
"object": "list",
"total_cards": 248583,
"has_more": true,
"next_page": "https://api.scryfall.com/cards?page=4",
"data": [
{
"object": "card",
"id": "18794d78-5aae-42de-a45b-3289624689f1",
"oracle_id": "a6543f71-0326-4e1f-b58f-9ce325d5d036",
"multiverse_ids": [
463813
],
"name": "Gateway Plaza",
"printed_name": "門前廣場",
"lang": "zht",
"released_at": "2019-05-03",
"uri": "https://api.scryfall.com/cards/18794d78-5aae-42de-a45b-3289624689f1",
"scryfall_uri": "https://scryfall.com/card/war/246/zht/%E9%96%80%E5%89%8D%E5%BB%A3%E5%A0%B4?utm_source=api",
"layout": "normal",
"highres_image": false,
"image_uris": {
"small": "https://img.scryfall.com/cards/small/front/1/8/18794d78-5aae-42de-a45b-3289624689f1.jpg?1556241680",
"normal": "https://img.scryfall.com/cards/normal/front/1/8/18794d78-5aae-42de-a45b-3289624689f1.jpg?1556241680",
"large": "https://img.scryfall.com/cards/large/front/1/8/18794d78-5aae-42de-a45b-3289624689f1.jpg?1556241680",
"png": "https://img.scryfall.com/cards/png/front/1/8/18794d78-5aae-42de-a45b-3289624689f1.png?1556241680",
"art_crop": "https://img.scryfall.com/cards/art_crop/front/1/8/18794d78-5aae-42de-a45b-3289624689f1.jpg?1556241680",
"border_crop": "https://img.scryfall.com/cards/border_crop/front/1/8/18794d78-5aae-42de-a45b-3289624689f1.jpg?1556241680"
},
"mana_cost": "",
"cmc": 0,
Some object on your response does not have the image_uris property so it throw error.
Add these line
let filtered = cards1.filter(card => card.image_uris);
And then map over filtered array, you will get what you need
let cardItems = filtered.map(card => {
return (
<li>
<img src={card.image_uris.png}/>
</li>
);
});

Categories

Resources