My GET request with axios returns back undefined in my console. All of my endpoints are good and
working from being tested with postman. My initial state go from pets: [] to pets: "". I think it's how I have my async await function set up to get the response data.
Here's the GET Component code
import React, {
Component
}
from 'react';
import axios from 'axios';
export default class ListPets extends Component {
constructor(props) {
super(props);
this.state = {
pets: [],
isLoaded: false,
}
}
componentDidMount = () => {
this.getPets();
};
getPets = async() => {
const res = await axios.get('http://localhost:5000/pets/');
const pets = res.data;
this.setState({
isLoaded: true,
pets: pets
});
console.log('Data has been received!');
console.log(pets.data)
return pets;
}
render() {
console.log('State: ', this.state);
const {
isLoaded,
} = this.state;
if (!isLoaded) {
return <div> Loading... </div>;
} else {
return (<div></div>);
}
}
}
app.get('/pets', function(req, res){
const resultArray = [];
client.connect(err => {
assert.equal(null, err);
console.log("Connected successfully to server");
const db = client.db(dbName);
const cursor = db.collection('pet').find({});
iterateFunc = (doc,err) => {
assert.equal(null, err);
resultArray.push(doc);
console.log(JSON.stringify(doc, null, 4));
if(err) {
console.log(err)
}
}
cursor.forEach(iterateFunc);
client.close();
res.render('index', {pets: resultArray});
});
});
Your code can only render a Loading message but if the problem is the empty string then my guess is your API is returning an empty string.
Here's your code, slightly modified but working with a JSONPlaceholder API: https://codesandbox.io/s/compassionate-faraday-h9xpg
export default class ListPets extends Component {
constructor(props) {
super(props);
this.state = {
pets: [],
isLoaded: false
};
}
componentDidMount = () => {
this.getPets();
};
getPets = async () => {
const res = await axios.get("https://jsonplaceholder.typicode.com/todos");
const pets = res.data;
this.setState({ isLoaded: true, pets: pets });
};
render() {
const { isLoaded, pets } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
}
return <>{pets && pets.map(pet => <div key={pet.id}>{pet.title}</div>)}</>;
}
}
Related
import React, { Component } from "react";
import axios from "axios";
class Verifry extends Component {
constructor(props) {
super(props);
this.state = {
s: "0",
user: [],
};
}
/* has title as attribute within the res.data*/
async componentDidMount() {
await axios
.get(http://10.0.0.106:8080/kuwait_elections/api/about_us)
.then((res) => {
const persons = res.data;
this.setState({ user: persons.data.title, s: "4" });
console.log(this.state.user);
});
}
componentDidUpdate() {
// this.state.user.map((u) => {
// return u;
// });
}
render() {
return (
{this.state.user.map((t) => {
return {t.title};
})}
);
}
}
export default Verifry;
Seems your return is not correct. It should be like this.
{
this.state.user.map(({title}) => {
return { title };
})
}
Note: Please format your code properly to make it easier to understand.
import React, { Component } from "react";
import axios from "axios";
class Abc extends Component {
constructor(props) {
super(props);
this.state = { descriptions: [] };
}
componentDidMount() {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then(response => {
this.setState({ descriptions: response.data });
if (response.data) {
var rdata = response.data;
for (var r = 0; r < rdata.length; r++) {
if (r === 0) {
// console.log(rdata[r]);
// const {rdata} this.dataEle = rdata[r]
console.log(this.dataEle.name);
}
}
}
})
.catch(error => {
console.log(error);
});
}
render() {
const { dataEle } = this.setState;
return (
<div>
{dataEle.map((description, index) => (
<p key={index}>{description.description}</p>
))}
</div>
);
}
}
export default Abc;
dataEle is undefined in the first render (and any subsequent renders before it is fetched). You also don't destructure it correctly in your render function. I think you likely meant to destructure descriptions instead.
import React, { Component } from "react";
import axios from "axios";
class Abc extends Component {
constructor(props) {
super(props);
this.state = {
descriptions: [],
};
}
componentDidMount() {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then(response => {
this.setState({ descriptions: response.data });
// if (response.data) {
// var rdata = response.data;
// for (var r = 0; r < rdata.length; r++) {
// if (r === 0) {
// // console.log(rdata[r]);
// // const {rdata} this.dataEle = rdata[r]
// console.log(this.dataEle.name);
// }
// }
// }
})
.catch(error => {
console.log(error);
});
}
render() {
const { descriptions } = this.state;
return (
<div>
// {descriptions.map((description, index) => (
// <p key={index}>{description.description}</p> // response data objects don't have a description property!
// ))}
{descriptions[1] && descriptions[1].name}
</div>
);
}
}
export default Abc;
Also, the response data shape doesn't have a description property on it, but TBH I'm not really sure what you're even trying to do with the for-loop, it throws an error.
Quite a few problems in your code.
Presumably you intended:
const { dataEle } = this.setState;
to be
const { descriptions = [] } = this.state;
Please try that:
class Abc extends Component {
constructor(props) {
super(props);
this.state = {
descriptions: [] ;
}
}
componentDidMount() {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then(response => {
this.setState({ descriptions: response.data });
})
.catch(error => {
console.log(error);
});
}
//for mapping**
return (
<div>
{this.sate.descriptions.map((description, index) => (
<p key={index}>{description.description}</p>
))}
</div>
);
}
}
I am consoling state right after my function call in componentDidMount but it's giving data as EMPTY String.
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: ""
};
}
getData = () => {
functionApiCall().then(res => {
this.setState({
data: res.data
}); // Here the state is getting set
})
}
componentDidMount() {
this.getData();
console.log(this.state.data); //Empty string
}
render() {
return <></>;
}
}
export default App;
Any help will be appreciated.Thank you
Well, I think the api call is returning null , maybe change it like this
getData = () => {
functionApiCall().then(res => {
if(res && res.data) {
this.setState({
data: res.data
})// Here the state is getting set
}
}
}
Above should be fine, but just in case try this
getData = () => {
return new Promise(function(resolve, reject) {
functionApiCall().then(res => {
if(res && res.data) {
this.setState({
data: res.data
}, () => { resolve(res.data) })// Here the state is getting set
}
} });
}
And componentDidMount wait for your promise which resolves after state is set
async componentDidMount(){
await this.getData();
console.log(this.state.data) //NULL
}
setState is asynchronous so you cannot immediately access it.
You can render conditionally like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: null
};
}
getData = () => {
functionApiCall().then(res => {
this.setState({
data: res.data
});
});
};
componentDidMount() {
this.getData();
}
render() {
if (!this.state.data) {
return <div>Loading...</div>;
} else {
return <div>Data: {JSON.stringify(this.state.data)}</div>;
}
}
}
export default App;
Sample codesandbox with a fake api
I'm trying to do a basic API fetch and show that information onClick using a button called GENERATE. All it should do for now is show the first url in the json I receive.
Once that is achieved, I want it to show the next url on each click.
App.js
import React, { Component } from 'react';
import { ThemeProvider, createToolkitTheme } from 'internaltools/theme';
import { AppHeader } from 'internaltools/app-header';
const LIGHT_THEME = createToolkitTheme('light');
const DARK_THEME = createToolkitTheme('dark');
const API = 'https://hn.algolia.com/api/v1/search?query=';
const DEFAULT_QUERY = 'redux';
class App extends Component {
constructor(props) {
super(props);
this.state = {
hits: [],
isLoading: false,
error: null,
};
}
componentDidMount(){
this.setState({ isLoading: true });
fetch(API + DEFAULT_QUERY)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong with the API...');
}
})
.then(data => this.setState({ hits: data.hits[0], isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
render() {
const { hits, isLoading, error } = this.state;
return (
<>
<button onClick={hits.url}>GENERATE</button>
</>
);
}
}
Please help me find out why my button doesn't work. And how do I iterate over the urls on each click, i.e. show the next url from the json on each click. Thanks.
You should pass a function name to your onClick handler. Then in that function you can access the data you wanted.
enter code here
import React, { Component } from 'react';
import { ThemeProvider, createToolkitTheme } from 'internaltools/theme';
import { AppHeader } from 'internaltools/app-header';
const LIGHT_THEME = createToolkitTheme('light');
const DARK_THEME = createToolkitTheme('dark');
const API = 'https://hn.algolia.com/api/v1/search?query=';
const DEFAULT_QUERY = 'redux';
class App extends Component {
constructor(props) {
super(props);
this.state = {
hits: [],
isLoading: false,
error: null,
hitsCount: 0
};
this.handleClick = this.handleClick.bind(this);
}
componentDidMount(){
this.setState({ isLoading: true });
fetch(API + DEFAULT_QUERY)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong with the API...');
}
})
.then(data =>
this.setState({ hits: data.hits, hitsCount: 0 ,isLoading: false
}))
.catch(error => this.setState({ error, isLoading: false }));
}
handleClick(){
this.setState(prevState => ({ hitsCount: prevState.hitsCount + 1
}));
}
render() {
const { hits, hitsCount, isLoading, error } = this.state;
return (
<>
<div>
count: {hitsCount}
url: {hits[hitsCount].url}
</div>
<button onClick={this.handleClick}>GENERATE</button>
</>
);
}
}
You need to pass an onClick handler function to update a state value.
Here's a codesandbox that stores the hits array in state along with a current index, and a handler that simply increments the index.
Consider This:
Read through the comments in the code to get the updates.
class App extends Component {
constructor(props) {
super(props);
this.state = {
hits: [],
currentHit: 0, //add a state currentHit to hold the url that is displayed by now
isLoading: false,
error: null,
};
}
componentDidMount(){
this.setState({ isLoading: true });
fetch(API + DEFAULT_QUERY)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong with the API...');
}
})
.then(data => this.setState({ hits: data.hits, isLoading: false })) //Make hits array holding all the hits in the response instead of only the first one
.catch(error => this.setState({ error, isLoading: false }));
}
handleClick = () => {
this.setState(prevState => ({
currentHit: prevState.currentHit + 1,
}));
}
render() {
const { hits, isLoading, error, currentHit } = this.state;
// pass the handleClick function as a callback for onClick event in the button.
return (
<>
<p>{hits[currentHit].url}<p/>
<button onClick={this.handleClick.bind(this)}>GENERATE</button>
</>
);
}
}
Here is the working code, on each click next url will be shown.
codesandbox link
handleChange method can work if you want to append the url from array as well. Or you could just increment the index in this function.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
state = {
data: [],
index: 0
};
componentDidMount() {
this.setState({ isLoading: true });
fetch("https://reqres.in/api/users")
.then(response => {
if (response) {
return response.json();
} else {
throw new Error("Something went wrong with the API...");
}
})
.then(data => this.setState({ data: data.data }))
.catch(error => this.setState({ error }));
}
handleChange = () => {
let i =
this.state.index < this.state.data.length ? (this.state.index += 1) : 0;
this.setState({ index: i });
};
render() {
return (
<div className="App">
<span>
{this.state.data.length && this.state.data[this.state.index].avatar}
</span>
<button onClick={this.handleChange}>GENERATE</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
import React, { Component} from 'react';
import { Route } from 'react-router';
import axios from 'axios';
class App extends Component {
constructor(props) {
super(props);
this.state = {
league: {
teams: {
data: [],
loaded: false,
config: {
icon: true,
parentId: 'leftSideTreeView'
}
},
players: {
data: [],
loaded: false,
config: {
icon: true,
parentId: 'leftSideTreeView'
}
},
games: {
data: [],
loaded: false,
config: {
icon: true,
parentId: 'leftSideTreeView'
}
},
error: false
}
};
}
componentDidMount() {
this.getTeamsHandler();
}
getTeamsHandler = () => {
axios.get('/api/League/GetTeams')
.then((response) => {
let prevState = [...this.state.league.teams];
prevState.data = response.data;
prevState.loaded = true;
this.setState({ teams: prevState });
})
.catch((error) => {
this.setState({ error: error });
});
}
renderTeamsHandler = () => {
let games = this.state.league.games;
let content = null;
if (games.data.length > 0) {
content = games.data.map((team, index) => {
return <div key={index}>{team.teamName}</div>;
});
}
return content;
}
render() {
let Team = this.renderTeamsHandler();
return (
<div>
{Team}
</div>
);
}
}
export default App;
The Ajax call does set data to prevState.Data but by the time it gets to rendering it, the state is the same as before the Ajax call. It is very confused as this all looks correct to me. Is it potentially async issue? If that is the case, why previously what I've done calls like this and had no issue at all.
Thanks for any help in advance.
I suspect that there are two part of problems.
First,the setState in getTeamsHandler:
axios.get('/api/League/GetTeams')
.then((response) => {
let prevTeam = [...this.state.league.teams];
prevTeam.data = response.data;
prevTeam.loaded = true;
this.setState(prevState => ({
league: {
...prevState.league,
teams: prevTeam
}
})
})
.catch((error) => {
this.setState(prevState => ({
league: {
...prevState.league,
error: error
}
});
});
Second,I guess there are some mistakes in renderTeamsHandler.Fetch date and set them in team, but use group in renderTeamsHandler.And the group in state is .
renderTeamsHandler = () => {
let teams = this.state.league.teams;
let content = null;
if (teams.data.length > 0) {
content = teams.data.map((team, index) => {
return <div key={index}>{team.teamName}</div>;
});
}
return content;
}