In the 'myjson.com' website, I created a url 'https://api.myjson.com/bins/17qwmf' which returns an array to me. How to get an element with 'id: 1', the first element from the array. I'm trying to do it this way: 'https://api.myjson.com/bins/17qwmf/1' but I'm getting an error.
From the documentation it looks like it can be done: http://myjson.com/api
Code here: stackblitz demo
class Items extends Component {
constructor (props) {
super(props);
this.state = {
items: []
}
}
componentDidMount() {
const id = 1;
axios.get
axios({
url: `https://api.myjson.com/bins/17qwmf/${id}`,
method: "GET"
})
.then(response => {
console.log(response.data);
this.setState({
items: response.data
});
})
.catch(error => {
console.log(error);
})
}
render () {
return (
<div >
</div>
)
}
}
if there is no a router for getting an element by it id, you have to filter got array
class Items extends Component {
constructor (props) {
super(props);
this.state = {
items: []
}
}
componentDidMount() {
const id = 1;
axios.get
axios({
url: `https://api.myjson.com/bins/17qwmf`,
method: "GET"
})
.then(response => {
console.log(response.data);
this.setState({
items: response.data.filter(item => item.id === id)[0] // you will get a first element of got array
});
})
.catch(error => {
console.log(error);
})
}
render () {
return (
<div >
</div>
)
}
}
Please check this `https://api.myjson.com/bins/17qwmf?id=${id} if you want to fetch only element with id that ur passed
Related
I get data into the props of my component by using getStaticProps. I then want to filter that data before I use it in the component. Usually I'd do this in componentDidMount, but that's not possible as it seems like the props are populated after componentDidMount is called.
What's the best practice for working around this?
Here's my current code:
class Definition extends Component {
constructor({ router }, ...props) {
super(props);
this.state = {
songsArray: [],
};
}
filterSpotifyResults = () => {
const filteredArray = [];
this.props.songsData.tracks.items.forEach((obj) => {
if (obj.explicit === true) {
return;
} else {
filteredArray.push(obj);
}
});
this.setState({ songsArray: filteredArray });
};
componentDidMount = () => {
this.filterSpotifyResults();
};
render() {
if (this.props.router.isFallback) {
return <h4>Loading...</h4>;
}
return (
<div>
<h3>this is where the definition will go</h3>
<ul>
{this.props.wordsData.definitions.map((obj, i) => (
<li key={i}>{obj.definition}</li>
))}
</ul>
<iframe
src={`https://open.spotify.com/embed/track/${this.props.songsData.tracks.items[0].id}`}
width="300"
height="380"
allowtransparency="true"
allow="encrypted-media"
></iframe>
</div>
);
}
}
export default withRouter(Definition);
export async function getStaticProps(context) {
const wordsRes = await fetch(
`https://wordsapiv1.p.rapidapi.com/words/${context.params.word}/definitions`,
{
method: "GET",
headers: {
"x-rapidapi-key": process.env.NEXT_PUBLIC_DB_KEY,
"x-rapidapi-host": "wordsapiv1.p.rapidapi.com",
},
}
)
.then((response) => {
return response;
})
.catch((err) => {
console.error(err);
});
const songsRes = await fetch(
`https://api.spotify.com/v1/search?q=${context.params.word}&type=track`,
{
method: "GET",
headers: {
authorization:
"Bearer " + process.env.NEXT_PUBLIC_ENV_SPOTIFY_ACCESS_TOKEN,
},
}
)
.then((response) => {
return response;
})
.catch((err) => {
console.error(err);
});
const wordsData = await wordsRes.json();
const songsData = await songsRes.json();
return {
props: {
wordsData,
songsData,
searchTerm: context.params.word,
},
};
}
Best practice would definitely be filtering the data on the server, already in your getStaticProps.
So move the filtering there, and only return the data you actually want to use/render.
In the below compoenent, the function is neverending. Can someone tell me what to fix so that in the end the beers array in the state has 5 names?
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
didError: false
};
this.getBeerInfo = this.getBeerInfo.bind(this);
}
render() {
return (
...
}
getBeerInfo() {
let beerArr = [1,2,3,4,5];
this.props.beerArr.map(id => {
fetch(`https://api.punkapi.com/v2/beers/${id}`)
.then(res => res.json())
.then(json => {
this.setState(state => {
const beers = state.beers.concat(json[0].name);
return {
beers
};
});
})
.catch(err => {
this.setState({
didError : true
});
});
})
}
}
Well your code should be somethings like this ..
import React from 'react';
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
didError: false
};
this.getBeerInfo = this.getBeerInfo.bind(this);
}
render() {
return (
<div>{this.state.beers}</div>
)
}
componentDidMount() {
this.getBeerInfo()
}
getBeerInfo() {
let beerArr = [1,2,3,4,5];
beerArr.map(id => {
fetch(`https://api.punkapi.com/v2/beers/${id}`)
.then(res => res.json())
.then(json => {
this.setState({
//const beers = state.beers.concat(json[0].name);
//return {
//beers
//};
beers: this.state.beers.concat(json[0].name)
});
console.log('well at least this works')
})
.catch(err => {
this.setState({
didError : true
});
});
})
}
}
It is advised that you use the componentDidMount() lifecycle method for the fetch api and add what #atahnksy said.
When you are using setState, you can try this:
this.setState({ beers: [...this.state.beers, json[0].name])
This might fix your problem.
You can improve the render method using a combination of ternary operator(to display appropriate message when it cannot reach the server), format with map and ordered list to get something like this :
render() {
return (
<div><ol>{this.state.beers.length!==0 ? this.state.beers.map((beer)=><li>{beer}</li>) :"Could not retrieve any bears. Try again/ensure you can access the server/networtk"}</ol></div>
)
}
I am trying to filter an array and set its state with the filtered version of that array. My code looks like this:
class Overview extends Component {
constructor() {
super();
this.state = {
card: []
}
}
deleteItem(id) {
fetch('/api/v1/story/'+id,{
method: 'DELETE',
})
.then(response => response.json())
.then(response => {
if(response['response'] == "success"){
this.setState({
//this is where it says this.state is undefined
card: this.state.card.filter(s => s.storyId !== id)
});
}else{
toastr.warning('dit item is al verwijderd', '', {positionClass: "toast-bottom-right", timeOut: 40000})
}
})
}
componentDidMount() {
fetch('/api/v1/overview')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
card: responseJson
})
})
}
render(){return (
<div className="deleteItem" onClick={deleteItem}>
</div>
)}
What happens here is that the page loads and fills the cards array (which works), the cards then get loaded in the DOM and when u click on an icon it should filter out the removed card from the card array and then set the state to the filtered array.
But whenever i get to this.setstate and try to filter it gives me this error:
app.js:66418 Uncaught (in promise) TypeError: Cannot read property 'card' of undefined
I hope i explained it good enough and that someone can help me with this. Thanks in advance!
Try this.
Also, why you make 2 .then()? Why not just one and use ()=>{and here you can write more than one line}
Edited: If you use arrow functions you don't need to bind the context of THIS
https://medium.com/byte-sized-react/what-is-this-in-react-25c62c31480
If you don't want to use arrow functions, you need to bind the context in the constructor
this.myFunction= this.myFunction.bind(this);
class Overview extends Component {
constructor() {
super();
this.state = {
card: []
}
}
deleteItem=(id)=> {
fetch('/api/v1/story/'+id,{
method: 'DELETE',
})
.then(response => response.json())
.then(response => {
if(response['response'] == "success"){
this.setState({
//this is where it says this.state is undefined
card: this.state.card.filter(s => s.storyId !== id)
});
}else{
toastr.warning('dit item is al verwijderd', '', {positionClass: "toast-bottom-right", timeOut: 40000})
}
})
}
componentDidMount() {
fetch('/api/v1/overview')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
card: responseJson
})
})
}
render(){return (
<div className="deleteItem" onClick={this.deleteItem}>
</div>
)}
I am working on a hacker news clone I am trying to get the ids of the top stories from their api using axios in componentDidMount and then making another axios call to get the stories and push them in a state array but when I try to map over and render that array nothing shows up
class App extends Component {
constructor(props) {
super(props);
this.state = {
posts: []
}
}
componentDidMount() {
axios.get('https://hacker-news.firebaseio.com/v0/topstories.json')
.then( result => {
result.data.slice(0, 10).forEach(element => {
axios.get('https://hacker-news.firebaseio.com/v0/item/' + element + '.json')
.then( value => {
this.state.posts.push(value)
})
.catch(err =>{
console.log(err)
})
})
})
.catch(err => {
console.log(err);
})
}
render() {
return (
<div>
<Header title="Hacker News" />
{this.state.posts.map( (element, index) => <Post key={element.data.id} serialNum={index} postTitle={element.data.title} postVotes={element.data.score} postAuthor={element.data.by} />) }
</div>
)
}
}
Try setting the state like this:
axios.get('https://hacker-news.firebaseio.com/v0/item/' + element + '.json')
.then( value => {
this.setState({
posts: [value, ...this.state.posts]
})
})
.catch(err =>{
console.log(err)
})
})
This way you're using setState and appending every new value to the existing state.
As stated in the comments, don't use push for set state. In your code when you make the second request you must change the setState method to spread out the new value.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: []
}
}
componentDidMount() {
axios.get('https://hacker-news.firebaseio.com/v0/topstories.json')
.then( result => {
result.data.slice(0, 10).forEach(element => {
axios.get('https://hacker-news.firebaseio.com/v0/item/' + element + '.json')
.then( value => {
this.setState(prevState => ({posts: [ value.data, ...prevState.posts]}))
})
.catch(err =>{
console.log("err");
console.log(err);
})
})
})
.catch(err => {
console.log(err);
})
}
render() {
return (
<div>
{this.state.posts && this.state.posts.map( (element, index) =>
<div key={element.id}>
{element.title}
</div>
)}
</div>
);
}
}
componentDidMount() is called after Render() only once. React doesn't know about the state changes unless you use setState().
componentDidMount() {
axios.get('https://hacker-news.firebaseio.com/v0/topstories.json')
.then( result => {
result.data.slice(0, 10).forEach(element => {
axios.get('https://hacker-news.firebaseio.com/v0/item/' + element + '.json')
.then( value => {
this.setState({posts: [value, ...this.state.posts]})
})
})
})
}
Use this.setState({posts : [value, ...this.state.posts]}) instead of this.state.posts.push(value). using ... (spread operator) appends the value to the original posts array.
In console.log the api fetched data are displaying but in browser itis
showing only white screen. In map function have to update the state function
import React, { Component } from 'react';;
import * as algoliasearch from "algoliasearch";
class App extends React.Component {
constructor() {
super();
this.state = {
data: { hits: [] }
}
// set data to string instead of an array
}
componentDidMount() {
this.getData();
}
getData() {
var client = algoliasearch('api-id', 'apikey');
var index = client.initIndex('');
//index.search({ query:""}, function(data){ console.log(data) })
//index.search({ query:""}, function(data){ console.log("DataRecib=ved. First check this") })
index.search({
query: "",
attributesToRetrieve: ['ItemRate', 'Color'],
hitsPerPage: 50,
},
function searchDone(error, data) {
console.log(data.hits)
});
}
render() {
return (
<div id="root">
{
this.state.data.hits.map(function (data, index) {
return
<h1>{this.setState.data.ItemRate}<br />{data.Color}</h1> >
})}
</div>
);
}
}
//render(<App />, document.getElementById('app'));
export default App;
Couple of mistakes -:
You just need to use this.state.data.ItemRate instead of this.setState.data.ItemRate.
You can get state inside .map using arrow functions ( . )=> { . }
Visit https://www.sitepoint.com/es6-arrow-functions-new-fat-concise-syntax-javascript/
render() {
return (
<div id="root" >
{
this.state.data.hits.map((data,index) => {
return<h1>{this.state.data.ItemRate}<br />{data.Color}</h1>
}
Every this.setState triggers a render() call. If you setState inside render method, you go into an infinity loop.
You want to update this.state.data.hits inside getData() function, then you can display the data like so:
this.state.data.hits.map(data =>
<h1>{data.Color}</h1>
)
For example, if console.log(data.hits) logs out the correct data, then you can:
this.setState({
data: {
hits: data.hits
}
})
EDIT:
Using the code you provided, it should be like this:'
getData = () => {
var client = algoliasearch('A5WV4Z1P6I', '9bc843cb2d00100efcf398f4890e1905');
var index = client.initIndex('dev_twinning');
//index.search({ query:""}, function(data){ console.log(data) })
// index.search({ query:""}, function(data){ console.log("Data Recib=ved. First check this") })
index.search({
query: "",
attributesToRetrieve: ['ItemRate', 'Color'],
hitsPerPage: 50,
}, searchDone = (error, data) => {
this.setState({
data: {
hits: data.hits
}
})
console.log(data.hits)
})
}