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.
Related
I have created a simple get request, but I am unsure what to do as it has asked for a key. This is a reduced version of my code:
import React, { Component, useEffect } from 'react';
import firebase from "gatsby-plugin-firebase";
var db = firebase.firestore();
var word1 = db.collection("menue").doc("word1");
class newMenue extends Component {
constructor(props) {
super(props)
this.state = {
wordOne: "",
}
}
render() {
word1.get()
.then(doc => {
if (!doc.exists) {
console.log('No such document!');
} else {
console.log('Document data:', doc.data());
this.setState({
wordOne: doc.data()
})
}
})
.catch(err => {
console.log('Error getting document', err);
});
return (
<div>
<h2 >{this.state.wordOne}</h2>
<br/>
<br/>
<h5>4 Slices of grilled Cypriot Halloumi</h5>
</div>
)
}
}
export default newMenue
this is my database:
Document data: {One: "hi"}
This is what the dev tools states.
Error: Objects are not valid as a React child (found: object with keys
{One}). If you meant to render a collection of children, use an array
instead.
What do I do in order to add the key to display the information?
Specifically, I am trying to set up a website for my friend's burger place where he can put in the menu changes on the website himself and I am stuck at this last bit.
You are trying to render an object:
<h2>{this.state.wordOne}</h2> // which is {One: "hi"}
You need to render the property of the object:
<h2>{this.state.wordOne.One}</h2>
I am trying to fetch from an api to and display the data im getting. I have tried using regular fetch with promises and axios and it doesnt seem to be doing anything. Im using the .map() method and I am getting the error map is not a function.
I have tried using fetch and axios to try and get the data from the api.
import React, { Component } from 'react';
class RecentNews extends Component {
state = {
recentArticles: [],
recentReports: [],
isLoaded: false,
}
componentDidMount(){
fetch(`https://spaceflightnewsapi.net/api/v1/articles?page=1`)
.then(response => response.json())
.then(json => this.setState(
{ recentArticles: json,
isLoaded: true,
}
))
}
render(){
let { recentArticles, isLoaded } = this.state
if (!isLoaded) {
return <h1>Loading.....</h1>
}
else {
return(
<div className="recentnews">
<div>
{ recentArticles.map(articles =>
<p>{articles.title}</p>
)
}
</div>
</div>
)
}
}
}
export default RecentNews
here is the error I'm getting
TypeError: recentArticles.map is not a function
▶ 17 stack frames were collapsed.
.map() is a function of Arrays in JavaScript - that API is returning an Object.
It would appear that what you want is the docs array inside that object, so try changing that line to:
{ recentArticles.docs.map(articles =>
The other keys in the object that is returned by that API relate to the pagination. You should use those to create pagination controls, for example, next page, previous page links etc.
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.
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>
)
}
}
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.