Getting undefined when trying to fetch data from an api - React - reactjs

So im trying to set a variable with the data im getting from the API.
when im console logging it into my browser everything works fine but when im trying to set my variable on React the variable ending up undifeind.
can someone tell me what am i missing here?
this is my code :
import React from 'react'
let news
function getNews () {
fetch(
'https://newsapi.org/v2/top-headlines?country=us&apiKey=6f9cf5e6b9684bd3a6a8117e35feb1c9'
)
.then(res => res.json())
.then(data => {
news = data
return news
})
}
getNews()
class NewsApi extends React.Component {
render () {
return <div />
}
}
export default NewsApi

Your getNews function is async. You should use state to save your data. So, as soon as the data fetched, you can use the data in your component.
import React from 'react';
class NewsApi extends React.Component {
constructor(props) {
super(props);
this.state = {
news: []
};
this.getNews = this.getNews.bind(this);
}
componentDidMount() {
this.getNews()
}
getNews() {
fetch('https://newsapi.org/v2/top-headlines?country=us&apiKey=6f9cf5e6b9684bd3a6a8117e35feb1c9')
.then(res => res.json())
.then((data) => {
this.setState({news:data.articles});
});
}
render() {
console.log(this.state.news)
return (
<div></div>
);
}
}
export default NewsApi;

try this : It outputs what you want.
** Notes:
Fetch is Async functions, means, it has to be called inside (for example) life cycle method like componentDidMount.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
news: []
};
}
componentDidMount() {
fetch(
"https://newsapi.org/v2/top-headlines?country=us&apiKey=6f9cf5e6b9684bd3a6a8117e35feb1c9"
)
.then(response => response.json())
.then(data => this.setState({ news: data.articles }));
}
render() {
console.log(this.state.news);
return <div />;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here is a live example (You can see console output also)\

Please look at the snippet below for an example implementation.
Some key points:
Try to do your data fetching in componentDidMount()
Use state and this.setState()
This will cause an automatic rerender after the data is fetched.
Handy for working with asynchronous function calls: these tools make it easy to use data in components without knowing when it will become available and help eliminate the undefined issue you were having.
class NewsApi extends React.Component {
state = {
articles: []
};
componentDidMount() {
fetch(
"https://newsapi.org/v2/top-headlines?country=us&apiKey=6f9cf5e6b9684bd3a6a8117e35feb1c9"
)
.then(res => res.json())
.then(data => data.articles)
.then(articles => {
this.setState({ articles });
});
}
render() {
return (
<div>
<h1>Articles</h1>
<ArticleList articles={this.state.articles} />
</div>
);
}
}
const ArticleList = props => (
<div>
<ol>
{props.articles.map((article, index) => (
<div key={index}>
<li>{article.title}</li>
<br />
</div>
))}
</ol>
</div>
);
function App() {
const appStyle = {
fontFamily: "sans-serif",
textAlign: "center"
};
return (
<div className="App" style={appStyle}>
<NewsApi />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related

Looking for clear,concise refactor of web hooks example

Ok so I am trying to understand React Hooks and how to update
my code to grab the JSON from the source below and show the data. I'm clear on importing the hook and initializing it with useState(0) but my code fails when I try to re-factor within my fetch statement. Any/all help would be greatly appreciated...see below.
// import React, { Component } from 'react';
import React, { useState } from 'react';
import Feeder from './Feeder';
import Error from './Error';
// class NewsFeeder extends Component {
// constructor(props) {
// super(props);
// this.state = {
// news: [],
// error: false,
// };
// }
const [hideNews,showNews] = useState(0);
componentDidMount() {
const url = `https://newsfeed.com`;
fetch(url)
.then((response) => {
return response.json();
})
.then((data) => {
this.setState({
news: data.articles
})
})
.catch((error) => {
this.setState({
error: true
})
});
}
renderItems() {
if (!this.state.error) {
return this.state.news.map((item) => (
<FeedPrime key={item.url} item={item} />
));
} else {
return <Error />
}
}
render() {
return (
<div className="row">
{this.renderItems()}
</div>
);
}
}
export default NewsFeeder;
React hooks are created for functional components and are not ment to be used in class components.
Here is a table of the functionality and the way to achive it using classes and functions with hooks.
component type
state
fetch
class
store the state in this.state that you only assign once in the constructor, use this.setState to modify the state
do your fetch logic in componentDidMount
function
create a pair of [example, setExample] with useState
do fetch in useEffect hook
Using fetch with hooks: (edited version of this):
import React, { useState, useEffect } from 'react';
function App() {
const [data, setData] = useState({ hits: [] });
useEffect(async () => {
const result = await fetch('https://hn.algolia.com/api/v1/search?query=redux').then(response => response.json());
setData(result);
});
let items = data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
));
return (
<ul>
{items}
</ul>
);
}
export default App;

React HOC with Router not finding page

Hi I am working on a react app with Routing and HOC. I expect to see a page but i get page not found when i know the page is there.
in componentDidMount this.setState, data is shown as undefined but in the HOC wrapper i see the data arrive from the server.
Before I wrapped the page in HOC i could see it rendering content so I know the content exists.
Here is my Page component which is being called via a Route :
import React, { Component } from "react";
import WithBackend from "./WithBackend";
class Page extends Component {
constructor(props) {
super(props);
this.state = { model: null };
}
render() {
if (this.state.model != null) {
return (
<div className="container">
<div className="row">
<div className="col-md">
<h1>{this.state.model.title}</h1>
</div>
</div>
</div>
);
} else {
return (
<div>
<h2>Home</h2>
</div>
);
}
}
componentDidMount() {
const data = this.props.getPage("1");
console.log(data);
this.setState({
model: data,
});
}
}
export default WithBackend(Page);
Here is the HOC component WithBackend: I am not sure if i should be setting the state on this class on in the class that is being wrapped.
When i debug the code in the getPage method, in the setState part i see the data being populated from the backend server.
import React from "react";
import ContentService from "./ContentService";
const WithBackend = (WrappedComponent) => {
class HOC extends React.Component {
constructor() {
super();
this.contentService = new ContentService();
this.getPage = this.getPage.bind(this); // <-- Add this
}
getPage(id) {
this.contentService
.getPage(id)
.then((response) => response.json())
.then((data) => {
this.setState({ model: data });
})
.catch((e) => {
console.log(e);
});
}
render() {
return <WrappedComponent getPage={this.getPage} {...this.props} />;
}
}
return HOC;
};
export default WithBackend;
and here is the contentService which only returns a promise:
class ContentService {
pageUrl = process.env.REACT_APP_BASE_URL + "/pages/";
getPage(id) {
const path = this.pageUrl + id;
const fetchPromise = fetch(path, {
method: "GET",
});
return Promise.resolve(fetchPromise);
}
}
export default ContentService;
Could anyone please advice what i am doing wrong?
Thanks in advance.
getPage is an asynchronous method, that should return a promise:
getPage(id) {
return this.contentService
.getPage(id)
.then((response) => response.json())
.catch((e) => {
console.log(e);
});
}
And then
componentDidMount() {
this.props.getPage("1").then(model => this.setState({ model }));
}

props.stories.map is not a function?

Hi I'm trying to make a simple news app to learn react. Keep getting an error which I don't understand. Why app worked fine with a json placeholder api. However now it's displaying the error props.stories.map is not a function.
Here is my code.
import NewsList from './components/NewsList';
class App extends React.Component {
constructor() {
super()
this.state = {
stories: [],
}
}
componentDidMount() {
fetch(`https://newsapi.org/v2/top-headlines?country=gb&apiKey=${API_KEY}`)
.then(res => res.json()
.then(data => this.setState({stories: data}))
)
}
render(){
const { stories } = this.state
return (
<div className="App">
<NewsList stories={stories} />
</div>
);
}
}
import Story from './Story'
import styled from 'styled-components'
const NewsList = props => {
return(
<NewsListWrapper>
{
props.stories.map(story => (
<Story story={story} />
)
)
}
</NewsListWrapper>
)
}
import React from 'react';
import styled from 'styled-components';
export const Story = props => (
<StoryWrapper>
<h2>{props.story.title}</h2>
<p>{props.story.author}</p>
<p>{props.story.articles}</p>
</StoryWrapper>
)
I have console.log the api response and the data was being received fine. It's simply when I use this other api response.
And I've hidden my api_key just for this post, so it's not issues with that.
Thanks for any advice.
In New API documentation (https://newsapi.org/docs/endpoints/top-headlines) you can see that your request return object of form
{
status: string
totalResults: number
articles: Array
}
So you need to set to stories not data but data.articles:
fetch(`https://newsapi.org/v2/top-headlines?country=gb&apiKey=${API_KEY}`)
.then(res => res.json()
.then(data => this.setState({stories: data.articles}))
)
Try this,
import NewsList from './components/NewsList';
class App extends React.Component {
constructor() {
super()
this.state = {
stories: [],
storiesData:[]
}
}
componentDidMount() {
fetch(`https://newsapi.org/v2/top-headlines?country=gb&apiKey=${API_KEY}`)
.then(res => res.json()
.then(data => this.setState({stories: data}))
)
}
render(){
const { stories } = this.state
return (
<div className="App">
<NewsList stories={stories} />
</div>
);
}
}
import Story from './Story'
import styled from 'styled-components'
const NewsList = props => {
console.log(props.stories);
this.setState({storiesData:props.stories});
return(
<NewsListWrapper>
{
this.state.storiesData.map(story => (
<Story story={story} />
)
)
}
</NewsListWrapper>
)
}
import React from 'react';
import styled from 'styled-components';
export const Story = props => (
<StoryWrapper>
<h2>{props.story.title}</h2>
<p>{props.story.author}</p>
<p>{props.story.articles}</p>
</StoryWrapper>
)

TypeError: this.state.monsters.map is not a function

I am Facing the problem like TypeError: this.state.monsters.map is not a function.I Have given this.state ={monsters:[]} After giving the state like above still i am facing that map is not a function....
import React, { Component } from 'react'
import './App.css';
class App extends Component {
constructor (){
super ()
this.state = {
monsters : []
};
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json)
.then(users => this.setState({monsters : users}))
}
render() {
return (
<div className="App">
{this.state.monsters.map(monster =>(
<h1 key={monster.id} > {monster.name} </h1>
))}
</div>
);
}
}
export default App;
Inovke response.json
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json()) // added parentheses
.then(monsters => this.setState({ monsters }))
}

How to pass parameters in API request in React?

I am making an API call in react where from a cryptocurrency API data will be fetched. But I want to get the data for specific cryptocurrencies. I am trying to define some logic inside the fetch request but it's not working. I am trying to add "bitcoin" and "ethereum" as a paramters inside the request.
Code:
import React, { Component } from 'react';
import './App.css';
import Crypto from './Component/Crypto';
class App extends Component {
constructor(){
super();
this.state={
data: [
{
name:'',
id:'',
symbol:'',
price_usd:'',
percent_change_1h:'',
percent_change_24h:'',
percent_change_7d:'',
isLoading:true
},
]
}
this.fetchData=this.fetchData.bind(this);
}
fetchData=()=>{
fetch('https://api.coinmarketcap.com/v1/ticker/?limit=3')
.then((response)=>{
const wanted=['ethereum','bitcoin']
const r=response.data.filter(currency=>
wanted.includes(currency.id))
this.setState({
data:r,
isLoading:false
})})
.catch(err=>alert("error"));
}
componentDidMount(){
this.fetchData();
this.interval = setInterval (() => this.fetchData (), 10*1000)
}
render() {
return (
<div className="App">
<div className="App-header">
{this.state.isLoading?<Loading/>:
<Crypto data={this.state.data}/>
}
</div>
</div>
);
}
}
const Loading=()=>{
return(
<div>
loading...
</div>
);
}
export default App;
try this way :
fetch('https://api.coinmarketcap.com/v1/ticker/?limit=3')
.then((resp) => resp.json())
.then(function(data){
const wanted=['ethereum','bitcoin']
const filtered=data.filter(currency=>{ return wanted.includes(currency.id)
});
console.log(filtered);
}).catch(err=>alert('error'));
learn more about fetch api here : https://scotch.io/tutorials/how-to-use-the-javascript-fetch-api-to-get-data

Resources