ReactJS: Fetching data from API - reactjs

I'm having difficulties fetching data from an API in a React app with a simple standard template for fetching API data. The returned result from the console logs are blank arrays.
import React, {Component} from 'react';
import './App.css';
import Chart from './components/chart'
const API_URL = "http://ergast.com/api/f1/2016/1/results.json";
class App extends Component {
constructor(props) {
super(props)
this.state = {
results: [],
};
}
componentDidMount() {
fetch(API_URL)
.then(response => {
if (response.ok) {
return response.json()
}
else {
throw new Error ('something went wrong')
}
})
.then(response => this.setState({
results: response.MRData
})
)}
render() {
const {results} = this.state;
return (
<div className="App">
<Chart data={results}/>
</div>
);
}
}
export default App;
chart.js
import React from 'react';
import {XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries} from 'react-vis';
const Chart = (props) => {
console.log(props.data);
const dataArr = props.data.map((d)=> {
return {x: d.RaceTable.Results.Driver.driverId, y: d.RaceTable.Results.position}
});
console.log(dataArr);
return (
<XYPlot
xType="ordinal"
width={1000}
height={500}>
<VerticalGridLines />
<HorizontalGridLines />
<XAxis title="Driver" />
<YAxis title="Race Finish Position" />
<LineSeries
data={dataArr}
style={{stroke: 'violet', strokeWidth: 3}}/>
</XYPlot>
);
}
export default Chart;
Really cannot figure out where I have gone wrong. I have set the state correctly with results: response.MRData isn't it? (MRData is the key of the JSON.) This is the structure of the json. API Link: http://ergast.com/api/f1/2016/1/results.json
JSON Example Response
{
"MRData": {
"xmlns": "http://ergast.com/mrd/1.0",
"RaceTable": {
"Races": [
{
"season": "2008",
"round": "1",
}
},
"Results": [
{
"position": "1",
"Driver": {
"driverId": "hamilton",
"permanentNumber": "44",
"code": "HAM",
"url": "http://en.wikipedia.org/wiki/Lewis_Hamilton",
"givenName": "Lewis",
"familyName": "Hamilton",
"dateOfBirth": "1985-01-07",
"nationality": "British"
},
}
},
.
.
.
]
}
]
}
}
}

Related

How to fix error when trying to render a list

I'm trying to render a list of movies, but the following error happens: "data.map is not a function". Although the API return is an array of objects.
API: enter link api
hook useRequestData:
import { useEffect, useState } from "react"
import axios from "axios"
export function useRequestData<T = unknown>(url: string) {
const [data, setData] = useState<T | null>(null)
useEffect(() => {
axios.get(url)
.then(response => {
setData(response.data)
})
}, [])
return { data }
}
import { useRequestData } from "../../hooks/useRequestData"
import { baseUrl } from "../../services/api"
import { Container } from "./style"
import movieImg from "../../assets/movie.svg"
type Films = {
id: number;
backdrop_path: string;
title: string;
release_date: string;
}
export const MovieCard = () => {
const { data } = useRequestData<Films[]>(baseUrl)
console.log(data)
return (
<>
{data?.map((films) => (
<Container key={films.id}>
<img src={films.backdrop_path} alt="" />
<h3>{films.title}</h3>
<p>12 NOV 2021</p>
</Container>
))}
</>
)
}
According to the API docs, your data will be an object that contains a results array.
Try data?.results.map instead of data?.map.
According to the API response return object. That means
response data = {
"page": 1,
"results": [
{
"poster_path": "/e1mjopzAS2KNsvpbpahQ1a6SkSn.jpg",
"adult": false,
"overview": "From DC Comics comes the Suicide Squad, an antihero team of incarcerated supervillains who act as deniable assets for the United States government, undertaking high-risk black ops missions in exchange for commuted prison sentences.",
"release_date": "2016-08-03",
"genre_ids": [
14,
28,
80
],
"id": 297761,
"original_title": "Suicide Squad",
"original_language": "en",
"title": "Suicide Squad",
"backdrop_path": "/ndlQ2Cuc3cjTL7lTynw6I4boP4S.jpg",
"popularity": 48.261451,
"vote_count": 1466,
"video": false,
"vote_average": 5.91
}
]
}
To access the results, use setData(response.data?.results) to set the value for data in your state if you only need result[] to MovieCard Component. If you want whole response for the MovieCard, set the state as you did and when access the array do like this.
return (
<>
{data?.results.map((films) => (
<Container key={films.id}>
<img src={films.backdrop_path} alt="" />
<h3>{films.title}</h3>
<p>12 NOV 2021</p>
</Container>
))}
</>
)
}

How to display geodjango rest api response on react leaflet Map

I have the following response from django rest:
[
{
"area": 0.0,
"perimeter": 0.0,
"town_name": "Cheptais",
"town_id": 4,
"town_type": "Market Centres",
"geom": "SRID=4326;MULTIPOINT (34.4500007629395 0.800000011920929)"
},
{
"area": 0.0,
"perimeter": 0.0,
"town_name": "Dadaab",
"town_id": 3,
"town_type": "Trading Centre",
"geom": "SRID=4326;MULTIPOINT (40.3199996948242 0.070000000298023)"
},
{
"area": 0.0,
"perimeter": 0.0,
"town_name": "Eldas",
"town_id": 4,
"town_type": "Market Centres",
"geom": "SRID=4326;MULTIPOINT (39.5499992370605 2.52999997138977)"
}
]
Fetching the endpoint with axios this way:
await axios
.get("/api/gis/areas/", headers)
.then((response) => {
this.setState({ places: response.data });
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
}
const handleEachFeature = (feature, layer) => {
layer.bindPopup('<font size="4">' + feature.properties.town_name);
}
Using react leaflet, I create a map instance as follows:
<Map className="map" onEachFeature={handleEachFeature} style={{height:'100%',width:'100%'}}>
<GeoJSON data={places}/>
</Map>
However, this does not overlay the api response on my map.. I'm I missing something?
as i mentioned on the comments,you have to convert the wkt to geojson in order for this to work, there are several solution to achieve such conversion but the easiest one here is to import wicket library (just use npm install wicket), also you need to create the with a unique key, here is a working component from your same data (note i'm not using axios as i test the data locally), :
import React, { Component } from 'react'
import './styles/styles.css'
import {Map,TileLayer,GeoJSON} from 'react-leaflet'
import './leaflet/leaflet.css'
import Wkt from 'wicket'
import L from 'leaflet'
import Data from '../../Data/wkt_file.json'
import icon from 'leaflet/dist/images/marker-icon.png';
import iconShadow from 'leaflet/dist/images/marker-shadow.png';
// this is for maker to show up:
let DefaultIcon = L.icon({
iconUrl: icon,
shadowUrl: iconShadow
});
L.Marker.prototype.options.icon = DefaultIcon;
export default class map extends Component {
constructor(props){
super(props);
this.state={
wkt_json_holder:[],
json_ob:<></>,
json_key:1,
tile:'https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png',
}
this.setGeoJSON = this.setGeoJSON.bind(this)
this.onEach = this.onEach.bind(this)
}
async componentDidMount(){
await this.setState({wkt_json_holder:Data});
this.setGeoJSON()
}
setGeoJSON=()=>{
let json_data = this.state.wkt_json_holder.map(point=>{
let wkt_geom = point['geom'].replace('SRID=4326;','')
let wkt = new Wkt.Wkt();
wkt.read(wkt_geom)
let geojson_geom = wkt.toJson()
let coords = geojson_geom['coordinates']
let type = geojson_geom['type']
let geojson_obj={
"type": "Feature",
"geometry": {
'type':type,
'coordinates':coords
},
"properties": {
"town_name": point['town_name'], "town_id": point['town_id'], "town_type":point['town_type'], "perimeter": point['perimeter'], "area": point['area']
}
}
return geojson_obj
}
)
console.log(json_data)
let json_ob= <GeoJSON data={json_data} key={1} style={{color:'red'}} onEachFeature={this.onEach}/>
this.setState({json_ob})
}
// handling Popups
onEach(feature,layer){
console.log(feature)
let PopupContent = `
<Popup>
<p>town id:${feature.properties.town_id}</p>
<p>town name:${feature.properties.town_name}</p>
</Popup>
`
layer.bindPopup(PopupContent)
}
render() {
return (
<div style={{width:'100%',height:'100%'}}>
<Map center={[2.197035, 38.703588]} zoom={6} style={{width:'100%',height:'100%'}}>
<TileLayer url={this.state.tile}/>
{this.state.json_ob}
</Map>
</div>
)
}
}

how to create React search with multiple fields name

i am working on autocomplete with reactjs and react material-ui. Now its working on only one field name symbol but i want its work on multiple fields name like "symbol and name" Here is my working code and API response. API response filed name return row.symbol;
React search code
import React, { Component } from "react";
import Autocomplete from "./Autocomplete";
import { render } from "react-dom";
import ApiService from "../../service/ApiService";
const style = {
flexGrow: 1,
};
export class SearchScripComponent extends Component<any, any> {
constructor(props: any) {
super(props);
this.state = {
searchArray: [],
message: null,
};
this.searchScripData = this.searchScripData.bind(this);
}
componentDidMount() {
this.searchScripData(this.requesDATA2());
}
requesDATA2() {
let data1 = { symbolOrName: "TATA" };
return data1;
}
searchScripData(searchScrip: any) {
ApiService.searchScripDataList(searchScrip).then((res) => {
this.setState({ searchArray: res.data.data });
});
}
render() {
const suggestions = this.state.searchArray.map((row: any) => {
return row.symbol;
});
return <Autocomplete suggestions={suggestions} />;
}
}
export default SearchScripComponent;
API Data
{
"statusCode": 200,
"message": "SUCCESS",
"data": [
{
"scripId": 299,
"symbol": "TATAGLOBAL",
"name": "abc"
},
{
"scripId": 520,
"symbol": "TATAYODOGA",
"name": "ttp"
},
{
"scripId": 1195,
"symbol": "TATASPONGE",
"name": "eer"
},
{
"scripId": 30,
"symbol": "TATASTLBSL",
"name": "qwer"
}
]
}

how to fetch api data in ReactJS?

i was trying to fetch api data in reactjs but data are not loading perfectly as expected, instead of i am getting an empty page as result. here below is my source code;
i am probably new to reactjs, it would be great if anybody could help me where i am doing thing wrong. thank you so much in advance.
endpoint_url : http://localhost:8000/api/blog_list
api-data:
[
{
"id": 1,
"url": "http://localhost:8000/api/blog_detail/brown",
"title": "brown",
"slug": "brown",
"image": "http://localhost:8000/media/blog/image_2.jpg",
"description": "",
"created_on": "2020-05-08T15:20:53Z",
"status": true,
"category": [
1
]
},
{
"id": 2,
"url": "http://localhost:8000/api/blog_detail/black",
"title": "black",
"slug": "black",
"image": "http://localhost:8000/media/blog/loc.png",
"description": "",
"created_on": "2020-05-08T17:14:31Z",
"status": true,
"category": [
2
]
}
]
./src/Base.js
export default class App extends Component{
state = {
bloglist:[]
};
componentDidMount(){
this.fetchData()
}
fetchData = async () => {
try{
const response = await fetch("http://localhost:8000/api/blog_list");
const jsonResponse = await response.json()
this.setState({bloglist:jsonResponse})
}
catch(error){
console.log(error)
}
}
render(){
const {bloglist} = this.state
if(!bloglist){
return (
<div>
<h1>loading...</h1>
</div>
)
}
return (
{
bloglist.map(bloglist => (
<h3 class="mb-2">
{bloglist.title}
</h3>
<p class="mb-4">{bloglist.description}</p>
))
}
)
}
}
Wrap it in a div. React expects a single element.
<h3 class="mb-2">
{bloglist.title}
</h3>
<p class="mb-4">{bloglist.description}</p>
Check this code.
export default class App extends Component {
state = {
bloglist: [],
};
componentDidMount() {
this.fetchData();
}
fetchData = async () => {
try {
const response = await fetch('http://localhost:8000/api/blog_list');
const jsonResponse = await response.json();
this.setState({ bloglist: jsonResponse });
} catch (error) {
console.log(error);
}
};
render() {
const { bloglist } = this.state;
var page = (
<div>
<h1>loading...</h1>
</div>
);
if (bloglist.length > 0)
page = bloglist.map((bloglistEntry) => {
return (
<React.Fragment key={bloglistEntry.id}>
<h3 className='mb-2'>
<a href='single.html'>{bloglistEntry.title}</a>
</h3>
<p className='mb-4'>{bloglistEntry.description}</p>
</React.Fragment>
);
});
return page;
}
}

Displaying Data from One Component in Another

I'm learning react at the moment and I'm trying to have two components interact with each other. The hierarchy is as follows:
App
--SearchForm
--Results
There's a data object that will be filtered through a string I enter in the SearchForm component. The filtered result should be displayed in the Results component.
My logic was to have all the functions needed in the App component, and pass the data to the individual components.
I want to be able to display the filtered data in the results component.
Can anyone help me with this please?
Please find the App.js file's code below, as well as a sample of the object I'm using.
App.js
import React, { Component } from "react";
import styled from "styled-components";
import Header from "./Header";
import SearchForm from "./SearchForm";
import Results from "./Results";
import Map from "./Map";
const Outer = styled.div`
text-align:center;
`;
class App extends Component {
constructor(props) {
super(props);
this.state = {
query: "",
data: [],
refinedData: [],
};
// this.handleSearchChange = this.handleSearchChange.bind(this);
}
handleSearchChange = (event) => {
this.setState({
query: event.target.value,
});
}
getData = async () => {
const response = await fetch("http://localhost:4200/bookings");
const json = await response.json();
this.setState({
data: json,
})
console.log(this.state.data);
}
filterData = () => {
const filtered = this.state.data.filter(element => {
return element.toLowerCase().includes(this.state.query.toLowerCase());
});
this.setState({
refinedData: filtered,
});
console.log(this.state.refinedData);
}
componentDidMount() {
this.getData();
}
render() {
return (
<Outer>
<Header/>
<SearchForm triggeredUpdate={this.handleSearchChange}/>
<Results searchQuery={this.state.filterData}/>
<Map/>
</Outer>
);
}
}
export default App;
Object
[
{
"id": 50000,
"car": {
"id": 1000,
"licence_plate": "SKK5050Q"
},
"book_start": 1543271643,
"book_end": 1543340723,
"pickup": {
"id": 87,
"code": "WDL",
"lat": 1.434,
"lng": 103.78
},
"dropoff": {
"id": 85,
"code": "TPY",
"lat": 1.33,
"lng": 103.851
},
"user": {
"id": 51498,
"name": "Count Dooku"
}
}
]
This is a simple logic actually in React. You want to show filtered results in your Results component, then you pass the filtered state to it. You can trigger the search with a button, then maybe the suitable place for this can be Search component. For this, you will pass your filterData method to it as a prop as you think.
I said a few times "it is an array not object" in my comments since the last data you show in your question says Object as bold but it is an array :) So, I got confused but you are doing it right.
You should filter your data with a prop in your object. As you think again, like user.name, car.license_late etc. You need a target here.
Here is a simple working example:
class App extends React.Component {
state = {
query: "",
data: [
{
"id": 50000,
"car": {
"id": 1000,
"licence_plate": "SKK5050Q"
},
"book_start": 1543271643,
"book_end": 1543340723,
"pickup": {
"id": 87,
"code": "WDL",
"lat": 1.434,
"lng": 103.78
},
"dropoff": {
"id": 85,
"code": "TPY",
"lat": 1.33,
"lng": 103.851
},
"user": {
"id": 51498,
"name": "Count Dooku"
}
}
],
refinedData: [],
};
handleSearchChange = event => this.setState({
query: event.target.value,
});
filterData = () => {
const { data, query } = this.state;
const filtered = !query ? [] : data.filter(element =>
element.car.licence_plate.toLowerCase().includes(this.state.query.toLowerCase())
);
this.setState({
refinedData: filtered,
});
}
render() {
return (
<div>
<SearchForm filterData={this.filterData} triggeredUpdate={this.handleSearchChange} />
<Results refinedData={this.state.refinedData} />
</div>
);
}
}
const Results = props => (
<div>
{
props.refinedData.map( el =>
<div key={el.id}>
<p>ID: {el.id}</p>
<p>User name: {el.user.name}</p>
</div>
)
}
</div>
)
const SearchForm = props => (
<div>
<input onChange={props.triggeredUpdate} />
<br />
<button onClick={props.filterData}>Search</button>
</div>
)
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Update after discussion on chat
You can do the search without a button while you typing. We don't have filterData method anymore since we moved the filter logic into handleSearchChange method. Also, we don't need any query state right now.
filterData array created with a ternary operator. If there is no search value we are returning an empty array since we don't want to list all of our data if there is not any search. By the way, I've updated my previous solution according to that, too. It was returning all the data if we hit the Search button with an empty input.
class App extends React.Component {
state = {
data: [
{
"id": 50000,
"car": {
"id": 1000,
"licence_plate": "SKK5050Q"
},
"book_start": 1543271643,
"book_end": 1543340723,
"pickup": {
"id": 87,
"code": "WDL",
"lat": 1.434,
"lng": 103.78
},
"dropoff": {
"id": 85,
"code": "TPY",
"lat": 1.33,
"lng": 103.851
},
"user": {
"id": 51498,
"name": "Count Dooku"
}
}
],
refinedData: [],
};
handleSearchChange = event => {
const { value: query } = event.target;
this.setState(prevState => {
const filteredData = !query ? [] : prevState.data.filter(element =>
element.car.licence_plate.toLowerCase().includes(query.toLowerCase())
);
return {
refinedData: filteredData
};
});
}
render() {
return (
<div>
<SearchForm triggeredUpdate={this.handleSearchChange} />
<Results refinedData={this.state.refinedData} />
</div>
);
}
}
const Results = props => (
<div>
{
props.refinedData.map(el =>
<div key={el.id}>
<p>ID: {el.id}</p>
<p>User name: {el.user.name}</p>
</div>
)
}
</div>
)
const SearchForm = props => (
<div>
<input onChange={props.triggeredUpdate} />
</div>
)
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Resources