Why is the data from my API is not display - reactjs

import React, { Component } from 'react'
class Newsgenerator extends Component {
constructor(){
super()
this.state = {
data:{}
}
}
componentDidMount() {
fetch("https://newsapi.org/v2/everything?q=Apple&from=2021-06-24&sortBy=popularity&apiKey={...}")
.then(response => response.json())
.then(news => {
this.setState({
data: news
})
console.log(news)
})
}
render() {
return (
<div>
{this.state.data.discription}
</div>
)
}
}
export default Newsgenerator

You have a few things going on here. First off, this.state.data.discription doesn't exist. When your API call response, you get an object like this which you set to this.state.data:
{
status: "ok",
totalResults: 1588,
articles: [...]
}
So this.state.data.discription is undefined. So what you're seeing is the result of trying to print an undefined variable - which in React is nothing.
My guess is that you want to print the articles, which means your return statement should look more like this:
return (
<div>
{this.state.data.articles.map((article, idx) => {
return (<h2 key={`article-${idx}`}>{article.title}</h2>)
}}
</div>
)
What you have in this code is an array of articles in your state.data, and you're iterating through that to print the article title (title in your api response).
This is the starting point and should set you in the right direction.

Related

TypeError: Cannot read property 'map' of undefined When trying to map multiple arrays

I am trying to map multiple arrays at the same time and im not sure if this is how you do it. I am getting the error
TypeError: Cannot read property 'map' of undefined
When trying the following code
import React, { Component } from 'react';
import Axios from 'axios';
import NavBar from '../header-footer/nav-bar'
import Featured from './FeaturedMealplan'
import RecipeItem from './RecipeItem'
export default class MealPlanDetail extends Component {
constructor(props) {
super(props);
this.state = {
currentId: this.props.match.params.slug,
mealplanItem: {}, // Full mealplan
mealplanRecipes: [], // Contains recipe names and difficulty.
}
}
getMealplanItem() {
Axios.get(`http://localhost:5000/get-mealplan/${this.state.currentId}`
).then(response => {
console.log("response", response)
this.setState({
mealplanItem: response.data.mealplan,
mealplanRecipes: this.state.mealplanRecipes.concat(response.data.mealplan["recipes"]),
mealplanIngredients: this.state.mealplanIngredients.concat(response.data.mealplan["recipe_info"]),
recipeItem: response.data.mealplan.recipes
})
}).catch(error => {
console.log("mealplan-detail GET Error ", error)
})
}
componentDidMount() {
this.getMealplanItem();
}
render() {
const renderRecipe = this.state.recipes.map((recipe, idx) => {
return (
<div key={idx}>
<h1>{recipe.recipe_name}</h1>
<h2>Recipe Difficulty: <span>{recipe.recipe_dificulty}</span></h2>
<div>
<RecipeItem recipeItem={this.state.recipeItem} />
</div>
</div>
)
})
return (
<div>
<NavBar/>
<Featured/>
{renderRecipe}
</div>
)
}
}
Data that is given: https://pastebin.com/uYUuRY6U
I just need to be able to format it correctly which this is how I would like it formatted in the renderRecipe return. I am new to mapping and do not know if there is a way to fix or a better way.
Some issues in the code that we can improve on:
this.state.recipes seems to be undefined in your logic. Is it a typo?
I would suggest implementing renderRecipe as a function instead of a variable.
You would only hope to render renderRecipe when there is data, but when your component is being mounted, this.state.recipes is undefined. It would only have value when getMealplanItem gets a response and being defined in the callback. So you should check whether the value is defined before rendering.
Please refer to my comments in the code below.
import React, { Component } from "react";
import Axios from "axios";
import NavBar from "../header-footer/nav-bar";
import Featured from "./FeaturedMealplan";
import RecipeItem from "./RecipeItem";
export default class MealPlanDetail extends Component {
constructor(props) {
super(props);
this.state = {
// ... define `recipes` if that's what you want
};
}
getMealplanItem() {
Axios.get(`http://localhost:5000/get-mealplan/${this.state.currentId}`)
.then((response) => {
console.log("response", response);
// ... set state `recipes` here if that's what you want
this.setState({
mealplanItem: response.data.mealplan,
mealplanRecipes: this.state.mealplanRecipes.concat(
response.data.mealplan["recipes"]
),
mealplanIngredients: this.state.mealplanIngredients.concat(
response.data.mealplan["recipe_info"]
),
recipeItem: response.data.mealplan.recipes
});
})
.catch((error) => {
console.log("mealplan-detail GET Error ", error);
});
}
componentDidMount() {
this.getMealplanItem();
}
render() {
const renderRecipe = () => {
// change renderRecipe from a variable to a function
if (!this.state?.recipes) {
// check whether `recipes` is a defined value
return null;
}
return this.state.recipes.map((recipe, idx) => {
return (
<div key={idx}>
<h1>{recipe.recipe_name}</h1>
<h2>
Recipe Difficulty: <span>{recipe.recipe_dificulty}</span>
</h2>
<div>
<RecipeItem recipeItem={this.state.recipeItem} />
</div>
</div>
);
});
};
return (
<div>
<NavBar />
<Featured />
{renderRecipe()} // it's a function call now
</div>
);
}
}
There is never a this.state.recipes defined. Based on data type and comment
this.state = {
currentId: this.props.match.params.slug,
mealplanItem: {}, // Full mealplan
mealplanRecipes: [], // Contains recipe names and difficulty.
}
I will assume you meant for it to really be this.state.mealplanRecipes.
Your render then becomes
const renderRecipe = this.state.mealplanRecipes.map((recipe, idx) => {...
This can easily handle the initial render with an empty array.

Error: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application

I'm getting the above error and I don't know how to handle it.
I got a component. And in the render() i'm looping through an array and placing another component and parsing a value to that component like this:
render() {
let allProducts = this.state.products.map((product, i) => {
return (
<div key={product.article}>
...
<PriceStock value={product.article} />
...
</div>
)
})
}
In the PriceStock component i'm fetching some data with axios like the code below:
export default class PriceStock extends React.Component {
constructor(props) {
super(props);
this.state = ({
buttoprice: ''
})
this.getPriceAndStock = this.getPriceAndStock.bind(this)
}
getPriceAndStock(articleNo) {
return axios.post('LINK_TO_URL', {
articleNo: articleNo
}).then(result => {
return result.data
})
}
async componentDidMount() {
let pricestock;
pricestock = await this.getPriceAndStock(this.props.value)
let bruttoPrice = PRICE_TO_PARSE_TO_THE_STATE;
this.setState({ buttoprice: bruttoPrice })
}
render() {
return (
<div >
{this.state.buttoprice}
</div>
);
}
}
The error seems to happen when I try to setState in the componentDidMount, any suggestions?
this is an error occurs because you are updating state before it gets initialized
perform your loading activities in the constructor it is the right way to do it
getPriceAndStock(orderNumber, articleNo) {
return axios.post('LINK_TO_URL', {
orderNr: orderNumber, vareNr: articleNo
}).then(result => {
return result.data
})
}
constructor() {
this.getPriceAndStock(this.props.value)
.then(pricestock=>{
let bruttoPrice = PRICE_TO_PARSE_TO_THE_STATE;
this.state({ buttoprice: bruttoPrice })
})
.catch(console.log)
}
Found the answear in this question: https://github.com/material-components/material-components-web-react/issues/434
It's remindend me a little bit about the comment with another stackoverflow question.

How to fetch data from api and pass it as props

I'm in the learning phase of react and trying to figure out how to
fetch api data and pass it as props, so i created my own api file in
github and tried to fetch the api data from it, here is the link
below:
https://raw.githubusercontent.com/faizalsharn/jokes_api/master/jokesData.js
for some reason the data is not being fetched from the api and not
being passed as props could someone, please explain me where im doing
wrong, forgive me if there is any obvious mistakes here im still in
beginner level
App.js
import React, {Component} from "react"
import Joke from "./joke"
class App extends Component {
constructor() {
super()
this.state = {
loading: false,
jokeComponents: []
}
}
componentDidMount() {
this.setState({loading: true})
fetch("https://raw.githubusercontent.com/faizalsharn/jokes_api/master/jokesData.js")
.then(response => response.json())
.then(data => {
this.setState({
loading: false,
jokeComponents: data.jokesData.map(joke => <Joke key={joke.id} question={joke.question} punchLine={joke.punchLine} />)
})
})
}
render() {
const text = this.state.loading ? "loading..." : this.state.jokeComponents
return (
<div>
{text}
</div>
)
}
}
export default App
joke.js
import React from "react"
function Joke(props) {
return (
<div>
<h3 style={{display: !props.question && "none"}}>Question: {props.question}</h3>
<h3 style={{color: !props.question && "#888888"}}>Answer: {props.punchLine}</h3>
<hr/>
</div>
)
}
export default Joke
I check the API, and found out that it is not working properly when the response.json() is being invoke in the fetch API.
And this is due to the error in the response of the API. You just need to return a bare array, and not return the API with a variable.
For reference, please check the return json of the Jsonplaceholder Fake API. https://jsonplaceholder.typicode.com/posts
Hope this fix your error.
Also, for the state of the jokeComponents, please just have the array passed in the response, and not manipulate the data. Just use the .map for the jokeArray in the render() function if the state is changed. :)
To show content after it is being loaded and hide the loading indicator, use a function that simulates an async action and after that the data will be shown. I've shown this example with another API, as there is a problem with your API. I hope you fix that. Also set headers to allow cross domain data access.
App.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Joke from "./Joke";
class App extends Component {
constructor() {
super();
this.state = {
loading: true,
jokeComponents: []
};
}
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/posts",{
headers: { crossDomain: true, "Content-Type": "application/json" }
}).then(response=>response.json())
.then(data => {
console.log(data);
this.setState({
jokeComponents: data.map(joke => (
<Joke
key={joke.id}
question={joke.title}
punchLine={joke.body}
/>
))
});
});
demoAsyncCall().then(() => this.setState({ loading: false }));
}
render() {
const { loading } = this.state;
if(loading) {
return "loading...";
}
return <div>{this.state.jokeComponents}</div>;
}
}
function demoAsyncCall() {
return new Promise((resolve) => setTimeout(() => resolve(), 2500));
}
ReactDOM.render(<App />, document.getElementById('root'));
The working code of the same is set up in CodeSandbox below:
Gideon Arces correctly explained your first bug, but there's more to do:
You need to format your .json file as json, which is not the same as javascript.
For example, while this is javascript {id: 1, question: "?"} it's not json. Json must be formatted like this: {"id": "1", "question":"?"} with quotes around the property names.
You need to do your data fetching in your componentDidMount and call setState there
You need to pull data from the state and render your components in render(). Typically this is done by creating an array of components and then putting them into the return inside {}. See more on that here: Lists and Keys
It's always a good idea to start with dummy data hardcoded into your component before you try to combine your ui with your api. See below in the componentDidMount() where I hardcoded some jokes. This way you can isolate bugs in your ui code from those in your network/api code.
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
jokes: []
};
}
componentDidMount() {
// this.setState({loading: true})
// fetch("https://raw.githubusercontent.com/faizalsharn/jokes_api/master/jokesData.js")
// .then(response => response.json())
// .then(data => {
// console.log(data);
// this.setState({
// loading: false,
// jokes: data
// })
// })
const json = `[
{
"id": "1",
"question": "?",
"punchLine": "It’s hard to explain puns to kleptomaniacs because they always take things literally."
},
{
"id": "2",
"question": "What's the best thing about Switzerland?",
"punchLine": "I don't know, but the flag is a big plus!"
}
]`;
const jokes = JSON.parse(json);
this.setState({ jokes });
}
render() {
const jokeComponents = this.state.jokes.map(joke => (
<Joke key={joke.id} question={joke.question} punchLine={joke.punchLine} />
));
console.log(jokeComponents);
const text = this.state.loading ? "loading..." : jokeComponents;
return <div>Text: {text}</div>;
}
}
function Joke(props) {
console.log("joke props:", props);
return (
<div>
<h3 style={{ display: !props.question && "none" }}>
Question: {props.question}
</h3>
<h3 style={{ color: !props.question && "#888888" }}>
Answer: {props.punchLine}
</h3>
<hr />
</div>
);
}

reactjs -- this.props being empty in componentDidMount

I've seen many posts regarding this.props, but none of them seems to answer my question, or at least I couldn't find it.
My component uses this.props as args to get my url
This following code is almost the exact copy of https://reactjs.org/docs/faq-ajax.html
import React, { Component } from 'react';
class MyList extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
componentDidMount() {
let entityKey = this.props.eKey;
fetch(`some/url/${this.props.eKey}`)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading My List...</div>;
} else {
if (!items) {
return <div>Failed to Load My List</div>;
} else {
return (
<ul>
{items.map(item => (
<li key={item}>
{item}
</li>
))}
</ul>
);
}
}
}
}
export default MyList
Now my script that calls this is simply
class MyFunc extends Component {
return (
<div>
<MyList
eKey={this.props.eKey}
/>
</dev>
);
}
I eliminated other code in MyFunc for simplicity
When I console.log my this.props inside MyList, it returns {eKey: ""}, so I know that the eKey is being passed in. However, as you can see, it's empty.
I don't know if it's because I have this at the componentDidMount cycle which for some reason this.props hasn't arrived when fetching. If that's the case, how do I guarantee the arrival before executing that line?
If it's somewhere else where I have issue, what went wrong?
EDIT:
To add in some more info. This is tied to a reducer. The redux is not exactly my strong suite.....so please correct me on any concept that's wrong.
The reducer takes the initialState and an action. The action can be different things. The only place that eKey is loaded is when action.type='load'
export function reducer(state = initialState, action) {
switch (action.type) {
case LOAD:
return {
...state,
eKey: action.data.eKey,
// and some other stuff
};
// some other cases here
default:
return state;
}
}
Interesting thing is there's a submit button that updates another component, which supposedly get a new eKey and get the eKey's data. At that time, the eKey is always populated, but my myList is not updated accordingly. I think it's another issue I have to figure out, but just wanna put it out here in case it's somehow related.
It seems like the reducer is not called only the component is mounted and componentDidMount is already called. And MyList does not update when the reducer is finally called. I'm not sure how to solve this problem
EDIT 2:
I tried moving what's inside componentDidMount into the render function (with some mod, of course). Does it work? sure, but when I console.log something, it just continues to print out stuff. Seems like this render function is constantly receiving update. I'm not sure if this is normal as my reactjs knowledge is not enough to answer this question. It works, but I'm wondering if this is the right way to do things
I solved my problem by calling another function, so my code looks like
class MyList extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
componentDidMount() {
this.props.loadMystuff().then(() => {
if (this.state.eKey !== this.props.eKey) {
let eKey = this.props.eKey;
this.fetchSList(eKey);
}
}).catch((error) => toast(error.message));
}
fetchSList(eKey) {
if (eKey !== '') {
fetch(`some_api_url_config/${this.props.entityKey}`)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
);
}
}
render() {
const { error, isLoaded, items } = this.state;
if (items) {
return (
<div>
<h3>S List</h3>
<ul>
{items.map(item => (
<li key={item}>
{item}
</li>
))}
</ul>
</div>
);
} else if (error) {
return <div>Error: List Missing...{error.message}</div>;
} else if (!isLoaded) {
return <div>Loading List...</div>;
} else {
return <div>Not Found</div>;
}
}
}
Note that the loadMyStuff is the one retrieving the eKey, so calling that will effectively get me the eKey to resolve the timing issue

Cannot read property 'map' of undefined with REACTJS

I am new with reactjs.
This is what I am trying
class EventDemo extends Component {
constructor(){
super()
this.getStarWars()
this.state = {}
}
getStarWars = ()=> axios.get('https://swapi.co/api/people')
.then(res => {
console.log(res.data)
this.setState({
names: res.data.results
})
})
render() {
console.log(this.state.names);
return (
<div>
{this.state.names.map(function(e){
return <li>{e.name}</li>
})}
</div>
);
}
}
But This following error i am getting
What I am doing wrong here ? It supposed to work .
First of all,you shouldn't call your this.getStarWars() function inside the constructor, it is a very bad practice and could cause you troubles, http calls in React component should be generally called from the componentDidMount function.
However the issue in this case is another one,you haven't given an initial value to this.state.names, so when the component tries to do the initial render it fails because the names are undefined since the initial render appens before the http call is resolved
You code should be fixed like this:
class EventDemo extends Component {
constructor(){
super()
this.state = { names:[] }
}
componentDidMount(){
this.getStarWars()
}
getStarWars = ()=> axios.get('https://swapi.co/api/people')
.then(res => {
console.log(res.data)
this.setState({
names: res.data.results
})
})
render() {
console.log(this.state.names);
return (
<div>
{this.state.names.map(function(e){
return <li>{e.name}</li>
})}
</div>
);
}
}

Resources