Need help to fetch json data with Axios dependancy for react - reactjs

I need help using the axios dependancy on react.
I'm fetching data from this endpoint: https://api.covid19api.com/summary
I setup a config for my API (config.jsx)
import axios from 'axios';
export default axios.create({
baseURL: `https://api.covid19api.com/summary`,
responseType: "json"
});
then i call it in my App, (a class component) as API
import API from './config.jsx';
and execute it within my ComponentDidMount()
componentDidMount() {
API.get().then(res => {
const countries = JSON.stringify(res.data.Countries);
//console.log(countries);
this.setState({ covid: countries });
console.log(`Etat du state: ${this.state.covid}`);
})
}
I get the data, store it in my state named 'covid', but when it comes to map over the results i get an error "TypeError Cannot read property 'map' of null" I think i have to convert the data into an array but i don't know how to do this .
render() {
return (
<div className="App">
<header className="App-header">
<h1>{this.state.appliname}</h1>
{this.state.covid.map(item => (
<div>{item.Country}</div>
))}
</header>
</div>
);
}
Here's the full script on codesandbox: https://codesandbox.io/s/intelligent-faraday-ykewv?file=/src/App.js
Thanks

There are several things you need to consider:
Always handle errors in promises. Sometimes you may face API failure, so you should handle the API request gets failed that we should do. So simply add a catch handler to your promise chain.
You should always handle first data initiation. In the first render of your page, there is no this.state.covid so you can't pass it to your view and map through it, so if you do this it will throw an error. To make this work you should add conditional rendering to your element.
Define your first state initiation correctly. Since you expect your this.state.covid to be an array, so you should define it as an array in the first place (this.state = {covid: []}).
Avoid passing strings to Array#map. When you try to convert your incoming items from API to JSON with JSON.stringify(res.data.Countries) it will make your data as JSON. Since JSON comes with string type it won't fit array helpers like Array#map, when you got your data there is no need to make JSON of the. If in any case, your incoming data was JSON itself you should parse it with JSON.parse().
Working demo:

Set init state default array
this.state = { covid: [], appliname: "CovidFacts" };
Set countries
const countries = res.data.Countries;
Follow all :
import React from "react";
import "./App.css";
import API from "./config.jsx";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
covid: [],
appliname: "CovidFacts"
};
}
componentDidMount() {
API.get().then(res => {
const countries = res.data.Countries;
this.setState({ covid: countries });
console.log(`Etat du state: ${this.state.covid}`);
});
}
render() {
return (
<div className="App">
<header className="App-header">
<h1>{this.state.appliname}</h1>
{this.state.covid.map(item => (
<div>{item.Country}</div>
))}
</header>
</div>
);
}
}
export default App;

Related

Results are not populating after fetching from API using React

I am working with some examples to fetch the data from an API using fetch. But, it is returning nothing in view. What i am doing wrong? Why it is not populating? Here is a link to the code.
Application code here:
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import axios from 'axios';
import './style.css';
const url='https://10degrees.uk/wp-json/wp/v2/posts';
class App extends Component {
constructor() {
super();
this.state = {
name: 'React',
searchInput: 'tirur',
avatarDatas: []
};
this.fetchData = this.fetchData.bind(this);
}
componentDidMount(){
this.fetchData(url);
}
fetchData = (apiToFetch)=>{
fetch(apiToFetch)
.then(result=>result.json())
.then(avatarData=>{
this.setState({
avatarDatas : avatarData
})
})
.catch((error) => console.log(error));
}
render() {
console.log(this.state.avatarDatas)
return (
<div>
<Hello name={this.state.name} />
<p>
{this.state.avatarDatas.map(item=>{
<div>{item}</div>
})}
</p>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Your code is fetching data from the remote URL correctly. However, you're using the map function in your render method wrong. You have:
{this.state.avatarDatas.map(item=>{
<div>{item}</div>
})}
This does indeed map through all the entries in avatarDatas, but the function the callback you've provided the map isn't returning anything. You've written a function block with a JSX statement in it, and no return statement. What you should have written is:
{this.state.avatarDatas.map(item=>{
return (<div>{item}</div>);
})}
or even this, where there isn't an actual function block but just a return value:
{this.state.avatarDatas.map(item=>
<div>{item}</div>
)}
At this point, the avatarDatas still won't be listed, because the JSX in your map callback is trying to have item rendered. item is an object, and React won't know how to convert it to a string. Rather do this:
{this.state.avatarDatas.map(item=>
<div>{item.title.rendered}</div>
)}
(Or any other of the many fields that each avatarData has.)
Finally, React may still issue a warning, saying that each item in a list must have a unique key. Whenever you use map to create a number of elements, React expects you to give a unique identifier to each element, which it will use to identify that element when it needs to update your list during rerendering. That means that you should do this:
{this.state.avatarDatas.map((item, index) =>
<div key={index}>{item.title.rendered}</div>
)}
Now, your map callback assigns an index to each <div> and all is well with the world.

How to print a list of objects from firebase in react?

I am getting the snapshot from the firebase and setting in the setState method and the in the render method i just want to print the list. In the first place because firebase is returning an object the map function was not working and i found something Array.from(this.state.testList) which seems to work but nothing is displayed. I am attaching the code and my firebase tree so you can see what I am trying to do.
25-10-2019:
firstPoint:
newsDesc: "Chris is promoted to scrum master!"
newsTitle: "Chris is promoted!!"
secondPoint:
newsDesc: "Christodoulos needed a change in his life"
newsTitle: "Christodoulos is leaving"
In the code above i am getting the snapshot of 'kabalaNews' which will return me and object with the data and this object will have many other objects with the points and the each point has a title and a description.
import React, { Component } from 'react'
import firebase from 'firebase';
import {DB_CONFIG} from './Config';
export class printFirebase extends Component {
constructor() {
super()
this.app = firebase.initializeApp(DB_CONFIG);
this.database=this.app.database().ref().child('kabalaNews');
this.state = {
testList: []
}
}
componentDidMount(){
this.database.on('value' , snap=> {
this.setState({
testList:snap.val()
});
});
}
render() {
console.log(this.state.testList);
return (
<div>
<div>
{Array.from(this.state.testList).map(news =>
<p>{news.25-10-2019.newsDesc} {news.25-10-2019.newsTitle}
</p>)}
</div>
</div>
)
}
}
export default printFirebase
You have a few mistakes:
I'd actually state the entire snapshot into the state, instead of just the values. This ensures you can meet two needs that you're bound to have in the future:
The items will show up in the order in which you queried them, instead of in alphabetical order of their keys.
You have access to the ID of each item, which is useful once you want to start manipulating the items.
You're using an invalid syntax for accessing properties here: news.25-10-2019.newsDesc. A JavaScript identifier cannot contain a -, so you'll need to use [] notation to access the value: news["25-10-2019"].newsDesc.
So in total that becomes:
componentDidMount(){
this.database.on('value' , snap=> {
this.setState({
testList:snap
});
});
}
render() {
console.log(this.state.testList);
return (
<div>
<div>
{this.state.testList.forEach(news =>
<p>{news.val["25-10-2019"].newsDesc} {news.val["25-10-2019"].newsTitle}
</p>)}
</div>
</div>
)
}
}

Having trouble fetching from an api not sure what I am doing wrong?

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.

React.js, correct way to iterate inside DOM

Im new in ReactJS...
I have a project with the following class components structure:
index.js
--app
--chat
--header
--left
--right
In the chat.js component, I make a google search with the api to retrieve images based on specific keyword... My intuitive solution was:
this.client.search("cars")
.then(images => {
for(let el of images) {
ReactDOM.render(<img src="{{el.url}}" syle="{{width: '100%'}}" />, document.querySelector('#gimages'));
}
});
It is correct? Or I may to use Components with stored states with flux (redux)?
Perhaps a simpler more conventional use of react would achieve what your require?
You could follow a pattern similar to that shown below to achieve what you require in a more "react-like" way:
class Chat extends React.Component {
constructor(props) {
super(props)
this.state = { images : [] } // Set the inital state and state
// model of YourComponent
}
componentDidMount() {
// Assume "client" has been setup already, in your component
this.client.search("cars")
.then(images => {
// When a search query returns images, store those in the
// YourComponent state. This will trigger react to re-render
// the component
this.setState({ images : images })
});
}
render() {
const { images } = this.state
// Render images out based on current state (ie either empty list,
// no images, or populated list to show images)
return (<div>
{
images.map(image => {
return <img src={image.url} style="width:100%" />
})
}
</div>)
}
}
Note that this is not a complete code sample, and will require you to "fill in the gaps" with what ever else you have in your current Chat component (ie setting up this.client)
This is not the way you should go, you don't need to use ReactDOM.render for each item. Actually, you don't need to use ReactDOM.render at all. In your component you can use a life-cycle method to fetch your data, then set it to your local state. After getting data you can pass this to an individual component or directly render in your render method.
class Chat extends React.Component {
state = {
images: [],
}
componentDidMount() {
this.client.search( "cars" )
.then( images => this.setState( { images } ) );
}
renderImages = () =>
this.state.images.map( image => <Image key={image.id} image={image} /> );
render() {
return (
<div>{this.renderImages()}</div>
);
}
}
const Image = props => (
<div>
<img src={props.image.url} syle="{{width: '100%'}}" />
</div>
);
At this point, you don't need Redux or anything else. But, if you need to open your state a lot of components, you can consider it. Also, get being accustomed to using methods like map, filter instead of for loops.

How to log exackly one object from fetched json data in react?

Is there a way to log 1 object or 2 or as much as i want to be logged in console?
Im using simple data from jsonplaceholder.typicode.com (quite usefull for learning purpose) which every of object has unique id.
For example:
I fetched data and rendered 200 posts on website, ok... but if i have a data which contains 100 logos or banners or layouts for website i want to render a specific logo or banner with unique id 30.
So, how can I render only 1st, 2nd, 3rd or x post/posts from 200?
This is what i have now:
App.js
import React, { Component } from 'react';
import './css/App.css';
import './css/bootstrap.css';
import $ from 'jquery';
import Todos from './Todos';
class App extends Component {
constructor(props) {
super(props);
this.state={
todos:[],
};
}
getTodos() {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/todos',
dataType: 'json',
cache: false,
success: function (data) {
this.setState({todos: data}, function () {
console.log(this.state);
});
}.bind(this),
error: function (xhr, status, err) {
console.log(err);
}
})
}
componentWillMount(){
this.getTodos();
}
componentDidMount() {
this.getTodos();
}
render() {
return (
<div>
<Todos todos={this.state.todos}/>
</div>
);
}
}
export default App;
Todos.js
import React, {Component} from 'react';
import TodoItem from './TodoItem';
class Todos extends Component {
render() {
let todoItems;
if(this.props.todos) {
todoItems = this.props.todos.map(todo => {
return (
<TodoItem key={todo.title} todo = {todo} />
);
});
}
return (
<div>
<h2>Todo list from api</h2>
{todoItems}
</div>
);
}
}
Todos.propTypes = {
todos: React.PropTypes.array
};
export default Todos;
TodoItem.js
import React, {Component} from 'react';
class TodoItem extends Component {
render() {
return (
<li>
<strong>{this.props.todo.title}</strong>
</li>
);
}
}
TodoItem.propTypes = {
todo: React.PropTypes.object
};
export default TodoItem;
#edit
Is there possible to filter mapped json data and find object depending on id and then render this object?
Slicing multiple items
You can use another state property, like filterCount, which can be either set by you manually, or you can trigger setState events from buttons e.g.
constructor(props) {
super(props);
this.setFilter = this.setFilter.bind(this);
this.state={
todos: [],
filterCount: 20 // Default value
};
}
setFilter(count) {
this.setState({ filterCount: count });
}
render() {
const { todos, filterCount } = this.state;
return(
<div>
...
<button onClick={this.setFilter(10)} />
<button onClick={this.setFilter(20)} />
<button onClick={this.setFilter(50)} />
</div>
)
}
This will ensure that your component is rerendered each time when you change the count.
Now the second part, filtering the first x items.
The main way is to use Array.prototype.slice
Slice example:
render() {
const { todos, filterCount } = this.state;
const filteredToDos = todos.slice(0,filterCount);
return(
<div>
...
{ filteredToDos.map(todo => {
return (
<TodoItem key={todo.title} todo={todo} />
);
}) }
</div>
)
}
Make sure you don't accidentally use splice instead of slice, because splice doesn't do the copy to a new array, but modifies the original one.
This example can be easily modified to support paging aswell. You could add a currentPage parameter to state, and just modify the slice line to slice the array based on which page you are.
Also, think about hard whether you need to use jQuery in your app. If you are using it only for convenience, then you are making a big mistake, as it is a hefty library that increases your bundle size considerably. Try to learn doing things the react way :)
Your ajax request can be done by using fetch api.
return fetch("https://jsonplaceholder.typicode.com/todos")
.then(response => response.json())
.catch((error) => {
console.error(error);
});
If you don't need out of the box support for the older browsers, fetch api will be fine for last 2-3 major versions of modern browsers, including mobile ones. It can be polyfilled for older ones such as IE8 aswell.
Also, there are libraries like axios that are actually much smaller then jQuery if you need more options and support for all request types and headers.
Also, the fetch action itself, could be decoupled from the component to a separate actions.js file so it can be imported / used from multiple components in the future, or refactored more easily to support working with something like Redux if your app grows.
Getting a single item
Fetching single item from API directly - suggested
If we are talking about performance, then the best way is to get a single item directly from API. Considering that this is a REST based api then the way would be:
https://jsonplaceholder.typicode.com/todos/{id}
Rewriten for a fetch example with a template literal:
return fetch(`https://jsonplaceholder.typicode.com/todos/${itemId}`)
.then(response => response.json())
.catch((error) => {
console.error(error);
});
This should also return a single object, not an array, so you should be able to pass it as a prop and use it immediately. IMHO, this is the best solution to the problem.
Fetching all items from API, then filtering with filter
Second option is to get all layouts, then filter the result which you can see in the example of: #mayank-shukla
Bottleneck of this method is that Array.prototype.filter can return multiple results.
Fetching all items from API, then filtering with find
Third option is to get all layouts, then filter the result with Array.prototype.find
Example is the same as filter, just change the keyword to find.
Find will return the first result in case of multiple ones.
Fetching all items from API, then filtering by index
Fourth option is getting all layouts, then filtering by index, e.g.
todos[itemId-1]
This will also work, but, you need to be 100% certain that backend will order the results in the array by their id. Also, index starts from 0, and your ids from 1, so this can be a source of major confusion.
Instead of passing all the data to child component filter out the data on the basis of condition and pass that filtered data to child component.
Like this:
render() {
//filter data of these ids
let ids = [1,2,3,4];
//filtered data
let data = this.state.todos.filter(el => ids.indexOf(el.id) != -1);
return (
<div>
<Todos todos={data}/>
</div>
);
}
If you want to pass first x data, the use #array.slice and pass the result, like this:
render() {
//filtered data
let data = this.state.todos.slice(0, 20);
return (
<div>
<Todos todos={data}/>
</div>
);
}
Instead of putting the filter part inside render you can do that after getting the response.

Resources