How to access elements of an array of JSON in reactjs? - reactjs

I am trying to fetch an array of JSON objects onto my react page. Now, the array I get is :
[{"_id":"5c4309fb643589310818165e","TableTime":"10:30- 11:00","TableType":"Pool Table 1","TableStatus":"Booked"},
{"_id":"5c430a3f2f788e322d2430d0","TableTime":"10:30- 11:00","TableType":"Pool Table 1","TableStatus":"Booked"}]
I first set it in a state variable result in componentDidMount() method.
In render method, when I do :
const { result } = this.state;
var slot = result;
console.log(slot);
I get the array as above. But when I do :
const { result } = this.state;
var slot = result;
console.log(slot[0].TableTime);
I get the error TypeError: Cannot read property 'TableTime' of undefined
I searched for it and tried this link and used JSON.parse(): Access elements of json in an Array in jquery
But then it gave me the error: SyntaxError: Unexpected token u in JSON at position 0
The relevant code is as follows :
class Timeslots extends Component {
constructor(props, context) {
super(props, context);
this.state = {
result: []
}
componentDidMount() {
this.getList();
}
getList = () => {
fetch("/purchase")
.then(res => res.json())
.then(result => this.setState({ result }))
}
render() {
const { result } = this.state;
var slot = result;
console.log(slot);
);
}
}
export default Timeslots;
I just want a way so that I can access the slot[0].TableTime and use it?

First the component renders. Then you get results from fetch . So you should have a null check in your render .
Something like return result !== null ? console.log(result):null
Also the slot variable is not required.

Related

Accessing a property that has an object as it's value

I'm working with an API that retrieves data from a request from a user. The data returned looks something like
{
name: "mewtwo"
id: 150
types: Array(1)
0:
slot: 1
type: {
name: "psychic", url: "https://pokeapi.co/api/v2/type/14"
}
}
I want to access the "psychic" value in type but I receive an undefined for "type". I'm able to display the name property and id just fine I just can't get the "psychic" value.
const Display = (props) => {
console.log(props.data.types);
return (
<>
<h1 className="pokemon-name">{props.data.name}</h1>
<p>{props.data.id}</p>
<p>{props.data.types.type.name}</p>//Will throw an error here
</>
);
}
You are trying to access an array element. Change it to the following
props.data.types[0].type.name
const Display = (props) => {
let { data } = props;
return (
<>
<h1 className="pokemon-name">{data.name}</h1>
<p>{data.id}</p>
<p>{data.types[0].type.name}</p>
</>
);
}
Since data.types is an Array of Objects, I think you want to access the first entry in the data.types array.
So I'd replace {props.data.types.type.name} with {props.data.types[0].type.name}.
For safety reasons I'd check for the existence of that array and extract the data out of it before using it like so:
const Display = ({data}) => {
// destructure properties out of data prop
const { name, id, types } = data;
// extract type name
const typeName = types[0].name;
return (
<>
<h1 className="pokemon-name">{name}</h1>
<p>{id}</p>
<p>{typeName}</p>
</>
);
}
Taking in account that your data is coming from an API, the types prop may not be populated when you're trying to access it. We can account for that scenario like so:
Here's a wrapper component example. this component gets the async data and renders the Display component.
// import as you would to make React work.
class Wrapper extends Component {
constructor(props) {
super(props);
this.state = {
dataFromApi: false
}
}
componentDidMount() {
// async function to get data from api (ex: fetch)
fetchDataFromAPI().then(res => res.json()).then(data => {
this.setState({ dataFromApi: data })
});
}
render() {
const { dataFromApi } = this.state;
return dataFromApi ?
(<Display data={dataFromApi}>) :
(<span>loading</span>);
}
}
Hopefully that makes sense 😅.
Cheers🍻!

ReactJS - Can't access properties in object from a fetch

I'm doing a fetch to an API and it's returning the data fine, but when I try access the properties, it returns:
Error: Objects are not valid as a React child (found: object with keys {breeds, categories, id, url, width, height}). If you meant to render a collection of children, use an array instead.
myFetch.jsx
import React, {Component} from "react"
class myFetch extends Component {
state={
data:[]
}
componentDidMount(){
const url = "xxxxxxxxxxxxxxxxxxxxxxx"
fetch(url)
.then(r=>r.json())
.then(data=>{
this.setState({data:data})
// console.log(data)
})
.catch(e=>console.log(e))
}
render(){
const {data} = this.state
console.log(data[0])
return (<p>{data[0]}</p>)
}
}
export default myFetch
EDIT
"data" in the state is initialized to an array. Therefore, I should have iterated through the array during the render as
{data.map(d => d.url)} and access whichever property I desire as shown below:
render(){
const {data} = this.state
console.log(data)
return (<p>{data.map(d=>d.url)}</p>)
}
Your data on the state doesn't have any element on 0 index. That's why you getting that undefined error. You can check if it exists before trying to render it.
Something like that:
render() {
const { data } = this.state;
if (data[0]) {
console.log(data[0]);
return <p>{data[0].url}</p>;
}
return null;
}

Unable to display data when define array index when using contentful cms

Unable to get data when using array index with contentful cms
constructor() {
super();
this.state = {
partners: [],
}
}
client = contentful.createClient({
space: process.env.REACT_APP_CONTENTFUL_SPACE_ID,
accessToken: process.env.REACT_APP_CONTENTFUL_ACCESS_TOKEN
})
componentDidMount() {
this.client.getEntries({
'content_type': 'partnersCollection',
'order': 'sys.createdAt',
})
.then(response => {
this.setState({
partners: response.items
})
})
.catch(console.error);
}
render(){
console.log("SIDE BAR RESULT: ", this.state.partners)
console.log(this.state.partners[0])
return null;
}
Console.log("SIDE BAR RESULT: ", this.state.partners) will display all the results from the contentful cms. When use with array index example this.state.partners[0].fields error will appear
Cannot read property 'fields' of undefined
Does anyone know why with the array index it will cause error message ?
At initial state - this.state.partners is empty array so finding this.state.partners[0].fields will give error
you need to put check in render
if(this.state.partners.length > 0) {
this.state.partners[0].fields
}
Once promise is resolved it will set the state.partners in componentDidMount(), render() will update itself.

REACTJS - Getting a TypeError for state variable

I'm try to get array data from a web service api (via JSON) and iterate through the response - but when trying to console.log() the state data from render(), I get this error:
TypeError:this.state.jsonStr is undefined.
class TextFields extends React.Component {
constructor() {
super();
this.state = {
}
};
componentDidMount() {
const apiURL = myConstClass.apiURL;
var apiURL_ = apiURL + '/searchIdea';
fetch(apiURL_, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
// SEND
idea_uniqueid: '',
idea_name: '',
idea_description: '', // FIX: Get from Querystring
})
}).then((Response) => Response.json()).then((findresponse) => {
// RESPONSE
this.setState({ jsonStr: findresponse, });
console.log("SEARCHIDEARESULTS"); // Stopped here - Loop through array and populate below
console.log(this.state.jsonStr);
})
}
// RENDER
// ---------------------------------------------------------------
render() {
const { classes } = this.props;
const dataStr = this.state.jsonStr;
console.log("RENDER"); // Stopped here - Loop through array and populate below
console.log(this.state.jsonStr);
console.log(this.state.jsonStr[0]);
return (
<div></div>
)
}
}
This happens because when the component is rendered for the first time the promise returned by fetch has not yet been resolved so the jsonStr is not found in the state. to fix this add jsonStr to the initial state and assign a value to it
TextFields extends React.Component {
constructor() {
super();
this.state = {
jsonStr: ''
}
}
}
or else you could also add conditions to check whether the jsonStr is set
TextFields extends React.Component {
constructor() {
super();
this.state = {
jsonStr: undefined
}
}
render() {
const { classes } = this.props;
const dataStr = this.state.jsonStr;
console.log("RENDER");
if (this.state.jsonStr) {
console.log(this.state.jsonStr);
console.log(this.state.jsonStr[0]);
}
return (
<div></div>
)
}
}
}
Initially this.state.jsonStr will be undefined. So when executing render() method this.state.jsonStr[0] will throw error.
Try changing console.log(this.state.jsonStr[0]); to console.log(this.state.jsonStr && this.state.jsonStr[0]);
Most of the answers touch on default state values or the error in render. But I see another error in your data fetching function.
From official documentation:
Calls to setState are asynchronous - don’t rely on this.state to reflect the new value immediately after calling setState.
In your componentDidMount method, you are making that mistake:
this.setState({ jsonStr: findresponse, });
console.log("SEARCHIDEARESULTS"); // Stopped here - Loop through array and populate below
console.log(this.state.jsonStr);
If you want to debug, I highly recommend the devTools extension, or put your console.log statements either as a callback to setState or in componentDidUpdate lifecycle.
Few notes on the advice for your render method, Do think of what your component states can be. For e.x. if fetching data from an external service, you may have the following states:
Loading
Error
No Data Found
Data Found
When you do: const dataStr = this.state.jsonStr ? this.state.jsonStr : "", you may lose the possibility to distinguish between these states.
I would advice to make that state state explicit (there are many techniques out there, I am leaving them out for brevity), but at the least I would suggest intelligent defaults. For e.x.:
constructor() {
super();
this.state = {
jsonStr : undefined
}
};
Your api call will either return value, null or an empty Array (if it is an array). May also throw an error, which you can catch with componentDidCatch.
You can now handle these cases in your render method easily.
Looks like you're trying to access to this.state.jsonStr before you define it and as a result you get the error:
TypeError:this.state.jsonStr is undefined.
Try setting it in the constructor:
constructor() {
super();
this.state = {
jsonStr: ''
}
};
Please try this modified code:
render() {
const { classes } = this.props;
const dataStr = this.state.jsonStr ? this.state.jsonStr : "" ;// I made a change here
console.log("RENDER"); // Stopped here - Loop through array and populate below
console.log(dataStr);
return (
<div></div>
)
}
}

props are undefined inside componentwillmount but shown inside render in typescript?

When props data are passed as props then it's undefined inside componentWillMount but defined inside render.
What might be the problem???
render:
public state: any = {
authority: [],
cid: "1",
data: [],
id: [],
menuTitle: []
};
public componentWillMount() {
var temp: any;
let url: String = "http://localhost:9000/getFieldTitle/";
fetch(url + this.state.cid + "")
.then((response) => response.text()).then((value) => {
let jsonObject = JSON.parse(value);
for (let index in jsonObject) {
for (let header in jsonObject[index]) {
temp = [];
if (header === "id") {
temp = this.state.id;
temp.push(jsonObject[index][header])
this.setState({ id: temp })
}
if (header === "menuTitle") {
temp = this.state.menuTitle;
temp.push(jsonObject[index][header])
this.setState({ menuTitle: temp })
}
if (header === "dataFormat") {
temp = this.state.data;
temp.push(jsonObject[index][header])
this.setState({ data: temp })
}
if (header === "authority") {
temp = this.state.authority;
temp.push(jsonObject[index][header])
this.setState({ authority: temp })
}
}
}
})
.catch(e => console.log(e));
}
public render() {
let row = []
for (let i = 0; i < this.state.authority.length; i++) {
row.push(<FormSection
key={i}
id={this.state.id[i]}
cid={this.state.cid[i]}
menuTitle={this.state.menuTitle[i]}
data={this.state.data[i]}
/>
)
}
return (
<div className="container-fluid">
{row}
</div>
);
}
FormSection.tsx:
<MenuSectionStructure data={this.props.data} check="check" />
MenuSectionStructure.tsx:
import * as React from "react";
export class MenuSectionStructure extends React.Component<any, any> {
public state: any = {
authority: [],
dataType: [],
fieldName: [],
};
constructor(props: any) {
super(props);
}
public componentWillMount() {
console.log(this.props.data) // Gives undefined
}
public render() {
return (
<div>{this.props.data}</div> //Gives value of this.props.data
);
}
}
I have shown all data
I think that your problem is definitely the one Max Sidwani commented. When you first load the parent component, you launch various setState in componentDidMount. Probably the header authority goes before the dataFormat one. This means that your component will re-render (and all its children) twice. The first time, authority.length will be an integer bigger than 0 and so the render will loop and try to render FormSection components where the data prop will be undefined because the dataFormat header hasn't already been processed and the data state is still []. Then, the data state is set and in the second re-render the data is not undefined. You can't watch two renders because the first one renders nothing and the second one happens inmediately after, but since you are using setState twice, render is being called twice (the first time with authority set and the second with data set). You can probably check this out with:
public componentWillUpdate() {
console.log(this.props.data) // Not undefined
}
in the MenuSectionStructure component.
You can solve it by setting both states at the same setState at the initial fetch or checking if data is not empty in the render.
Yes, I found the answer by sending the whole data as a single props and then parsing at the lowest component so that i could display all objects as a props in component as required. The problem was with sending multiple data as props and choosing one props as props length in loop which cause all problem since they all were all array and set state was hitting randomly causing the loop with unwanted sequence. However, sending the whole data as a single props and then looping inside came as a solution to my problem.
Thanks all for your contribution, which help me allot to visualize the reason of the scenario.

Resources