React App: How to display data from api using fetch - reactjs

I am trying to display school data from an external api using React. I'm just trying to display a school name to start. The school name appears in the console, but it doesn't show up in the browser.The api call is correct, as it works in Postman. Here is my code:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
schoolName: '',
// schoolData: {}
}
}
fetchSchool(event) {
event.preventDefault();
const apiKey = 'XdOHSc8fKhMKidPu2HWqCZmMy9OxtCJamGC580Bi';
const fields = `_fields=school.name,2015.aid.median_debt.completers.overall,2015.cost.tuition.in_state&school.name=${this.state.schoolName}`;
const requestUrl = `https://api.data.gov/ed/collegescorecard/v1/schools?&api_key=${apiKey}&${fields}`;
const school = fetch(requestUrl).then((res) => res.json()).then((data) => console.log(data.results[0]['school.name']));
this.setState({
schoolName: school
// schoolData: school
})
console.log(this.state.schoolName);
}
setSchool(event) {
event.preventDefault();
this.setState({
schoolName: event.target.value
});
}
render() {
// const schoolname = this.state.schoolName[0];
// const {schooName} = this.state;
return (
<div>
<form action="/school" method="GET" id="myform">
<input type="text" className="form-control" id="enter_text" onChange={this.setSchool.bind(this)} />
<button onClick={this.fetchSchool.bind(this)} type="submit" className="btn btn-primary" id="text-enter-button button submit">Submit</button>
</form>
<div>
<p>School: {this.state.school} </p>
</div>
</div>
);
}
}
export default App;

fetch is asynchronous. Therefore, setState is being called before the data has been fetched.
To fix this, call this.setState from inside of your then function
const school = fetch(requestUrl)
.then((res) => res.json())
.then((data) => {
console.log(data.results[0]['school.name'])
this.setState({
schoolName: data.results[0]['school.name'],
schoolData: data.results
})
});

In your render method change this line because schoolName is your state variable and not school.
<p>School: {this.state.school} </p>
to
<p>School: {this.state.schoolName} </p>

Related

Cannot insert input text to url in fetch api

I am trying to insert the value of my stateful variable (myValue) to the api url after q=
I tried using ${} but it is not working.
It is working when I insert text righ in to url, but I need to insert the input variable. ( sorry for messy code, I am a beginner )
p.s ignore the api key, I just replace it with *** here.
import React, { createRef } from "react";
import { Component } from "react";
import { Button, TextField } from "#mui/material";
import WeatherCard from './components/WeatherCard'
class App extends Component {
constructor(props) {
super(props);
this.textInput = createRef();
this.state = {
myValue: "",
temp : ""
};
}
showRefContent = () => {
this.setState({
myValue: this.textInput.current.value
});
const uriEncodedCity = encodeURIComponent(this.state.myValue);
fetch('https://api.openweathermap.org/data/2.5/weather?q=madrid&appid=***')
.then(response => response.json())
.then(data => this.setState({
temp : data.main.temp
}),
console.log(this.state.temp)
);
}
handleChange = (e) =>
this.setState({
myValue: e.target.value
});
render() {
return (
<div >
<div >
<TextField inputRef={this.textInput}
id="demo-helper-text-misaligned-no-helper" label="City name"
/>
<br></br>
<Button margin = "right:20" size="large" variant ="contained" onClick={this.showRefContent}>Get Weather</Button>
</div>
<p>
{this.state.myValue.length > 0
? <WeatherCard temp = {this.state.temp} />
: "no text"}
</p>
</div>
);
}
}
export default App;
Did you use `` or '' with ${}. Make sure you use ``.
fetch(`https://api.openweathermap.org/data/2.5/weather?q=${}&appid=***`)

State not fetching API data on submit

First stackoverflow post. New to React (1 week) and using API fetch requests to understand props, state and components.
I'm using a weather API to get the temperature for a particular city. User enters city into a form, clicks submit and the temperature is displayed.
When I hard code the selectedCity state by typing in a city as a test, I can see in the DevTools that the API data is fetched for that city and the city's temperature is displayed in the browser. The issue is when I click submit on my form component. On submit I can see in the DevTools that it updates the selectedCity state with user's city of choice but it doesn't fetch the data. I've noticed that when hard coding in the city that the page refreshes and the data is fetched and result displayed but when submitted via the form there is no refresh of the page.
I just don't know enough about React to figure out what is going on here. Grateful for any pointers.
This is my WeatherContainer
import React, { Component } from 'react';
import Headings from '../components/Headings';
import Form from '../components/Form';
import Weather from '../components/Weather';
class WeatherContainer extends Component {
constructor(props) {
super(props);
this.state = {
weatherData: [],
selectedCity: ""
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
fetchData() {
fetch(`https://api.openweathermap.org/data/2.5/weather?q=${this.state.selectedCity}&APPID=MyAPIKey`)
.then(res => res.json())
.then(result => this.setState({ weatherData: result.main }))
}
componentDidMount() {
this.fetchData();
}
handleFormSubmit({city}) {
this.setState({selectedCity: city})
}
render() {
return (
<div>
<Headings />
<Form onFormSubmit={this.handleFormSubmit} />
<Weather weather={this.state.weatherData} />
</div>
);
}
}
export default WeatherContainer;
This is my form component
import React, { Component } from 'react';
class Form extends Component {
constructor(props) {
super(props);
this.state = {
city: ""
};
this.handleCityChange = this.handleCityChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
const city = this.state.city;
if (!city) {
return
}
this.props.onFormSubmit({
city: city
});
this.setState({
city: ''
})
}
handleCityChange(event) {
this.setState({
city: event.target.value
})
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.city}
placeholder="Enter City"
onChange={this.handleCityChange}
/>
<input type="submit" value="Submit" />
{/* <button>Get Weather</button> */}
</form>
);
}
}
export default Form;
My Weather Component
import React from 'react';
const Weather = ({ weather }) => {
if(!weather) return null
return (
<div>
<h1>{weather.temp}</h1>
</div>
);
}
export default Weather;
So you have a componentDidMount method but you don't handle the updates of the state.
You need a componentDidUpdate method
componentDidUpdate(prevProps, prevState) {
if (prevState.city !== this.state.city) {
this.fetchData();
}
}

How to make an axios POST request in React?

So, some context: Users submit a dog name via a text input, and this is controlled by the 'Dogue.jsx' component:
import React from 'react';
class Dogue extends React.Component {
constructor(props) {
super(props);
this.state = {
id: props.id,
nameInput: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
nameInput: e.target.value,
});
}
handleSubmit(e) {
e.preventDefault();
this.props.inputFunction(this.state.nameInput);
}
render() {
console.log(this.props.id);
return (
<div className="dogue-container">
<img className="img" src={this.props.dogList} />
<br />
<form onSubmit={this.handleSubmit} className="form">
<input
onChange={this.handleChange}
className="input"
type="text"
placeholder="Enter dog name"
/>
<br />
<button className="button">Submit</button>
</form>
<h2 className="text">Name: {this.props.name} </h2>
</div>
);
}
}
export default Dogue;
The submitted information is then passed to 'App.jsx', where it is used to update state:
import React, {Component} from 'react';
import './styles.css';
import DogList from './DogList';
import axios from 'axios';
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
dog: [],
dogName: [],
};
this.updateStateWithInput = this.updateStateWithInput.bind(this);
}
setData = async () => {
const x = await fetch('https://dog.ceo/api/breed/hound/images');
const y = await x.json();
const z = await y.message;
let newArr = [];
for (let i = 0; i < z.length; i++) {
if (i <= 9) {
newArr.push(z[i]);
}
}
return newArr;
};
async componentDidMount() {
this.setState({
loading: true,
});
let dogPromise = await this.setData();
let dogNamePromise = await axios.get('http://localhost:3000/dogs');
this.setState({
loading: false,
dog: dogPromise,
dogName: dogNamePromise.data,
});
}
updateStateWithInput(nameInput) {
//Here is where state is updated.
//change state, then use axios.post to submit data
}
render() {
return this.state.loading ? (
<h1 className="text"> Dogues Loading.....</h1>
) : (
<div>
<h1 className="text">Rate My Dogue</h1>
<DogList
dogs={this.state.dog}
name={this.state.dogName}
inputFunction={this.updateStateWithInput}
/>
</div>
);
}
}
export default App;
The updated state, I imagine, will be used in the axios post request to submit data to the database. So, I've got input data being sent from Dogue to App, I'm just not sure what to do now? The information currently in state looks as follows:
[
{
id: 1,
dogName: 'bruce',
},
{
id: 2,
dogName: 'borker',
},
{
id: 3,
dogName: 'henry',
},
];
I should also show my map function, in DogList.jsx:
import React from 'react';
import Dogue from './Dogue';
const DogList = (props) => {
return (
<div className="img-container">
{props.dogs.map((doggie, index) => {
return (
<Dogue
id={props.name[index] && props.name[index].id}
key={index}
dogList={doggie}
name={props.name[index] && props.name[index].dogName}
inputFunction={props.inputFunction}
/>
);
})}
</div>
);
};
export default DogList;
You can send a POST request with axios by calling:
axios.post(url, data, options);
It’s similar to the way you called the get method to make a GET request.
I’m leaving this axios cheat sheet here since it’s really useful until you get the hang of it:
https://kapeli.com/cheat_sheets/Axios.docset/Contents/Resources/Documents/index

Can't render {this.state.city}

I'm creating a weather app in React using OpenWeatherMap API. There are input form and a button, and I'm expecting to see city name when I click the botton. I received data from the API when I do so, but can't render it on a screen while I can log it in a console.
For this, I'm using three separated files. App.js, Form.js for submitting terms, and weather.js for API configuration.
I'm guessing that I need to map the received data but not yet successful.
class App extends React.Component {
state = {
city: null,
}
getWeather = async city => {
const response = await weather.get('/forecast', {
params: {
q: city
}
});
this.setState({
city: response.name,
})
console.log(city); <--- This works
}
render() {
return (
<div>
<Form loadWeather={this.getWeather} />
<p>{this.state.city}</p> <--- This doesn't work
</div>
);
}
}
class Form extends React.Component {
state = { term: '' };
onFormSubmit = (event) => {
event.preventDefault();
this.props.loadWeather(this.state.term);
this.refs.textInput.value = '';
}
render() {
return (
<div>
<form onSubmit={this.onFormSubmit}>
<input
ref="textInput"
type="text"
value={this.state.term}
onChange={event => this.setState({term: event.target.value})}
/>
<button>Get Weather</button>
</form>
</div>
);
}
}
export default Form;
I'm going to pass the {this.state.name} as a prop to a child component, but so far the received data doesn't even appear on that component ifself.
"this.setState" is a function, should be called like this.
this.setState({
city: response.name,
})
You're setting state's city to response.name. You tagged the question as axios, so I'm assuming you're using axios for the ajax. If so, you'll want to get the data from the response back from response.data.name, not response.name.
If you want to get the city details from the initial render try calling/invoking the getWeather() method in componentDidMount() life cycle. The way you are using setState() is also wrong. it should be something like this as mentioned below. The same lifecyle could be used to get the data even though you have used a separate file to invoke the method
class App extends React.Component {
state = {
city: null
};
componentDidMount() {
this.getWeather("city");
}
getWeather = async city => {
const response = await weather.get("/forecast", {
params: {
q: city
}
});
this.setState({
city: response.name
});
};
render() {
return (
<div>
<p>{this.state.city}</p>
</div>
);
}
}

Correct way to React axios post with UUID and nested object in JSON payload?

My server-side application accepts an int, does some simple math, and returns an int as Content-Type application/json. The api has been tested with Postman and works correctly.
I'm looking for the proper way to handle an Axios POST with a JSON payload that includes a UUID with an object nested below it. As suggested, I added [''] around the UUID to play nicely with React. If I click 'Post' without entering a value my server returns an int for 'current_value'. If I enter a number in the field 'current_value' returns a string e.g., 4 + 2 = "42".
import React, { Component } from 'react';
import axios from 'axios';
class Post extends Component {
constructor() {
super();
this.state = {
current_value: 0
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = event => {
this.setState({ current_value: event.target.value });
console.log(event.target.value);
};
handleSubmit = event => {
event.preventDefault();
axios.post('http://my.server.url', {
foo: 'bar',
['e0ea641b-3de4-4a76-857d-11da9352698a']: { current_value: this.state.current_value }
})
.then(response => {
this.setState({ current_value: response.data.current_value });
console.log(JSON.stringify(response.data));
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>Input Number:
<input type="number" name="current_value" onChange={this.handleChange} />
</label>
<button type="submit">Post</button>
</form>
<div>
Output Number: { this.state.current_value }
</div>
</div>
);
}
}
export default Post;
Try to escape your uuid like below, it should work:
{
foo: 'bar',
['e0ea641b-3de4-4a76-857d-11da9352698a']:{ current_value: this.state.current_value }
}
With a nod to help from #GuilhermeLemmi, I've landed on the answer that addresses both my initial issue and the problem of handling the response where the item in question contains a minus sign -. Wrapping my UUID in [] in the data object wasn't necessary, but I did need to wrap it in single quotes. On the return side I did need to wrap the response in [''] but leave it as an object, don't JSON.stringify() it. Now everything flows nice and smooth.
import React, { Component } from 'react';
import axios from 'axios';
class Post extends Component {
constructor() {
super();
this.state = {
current_value: 0
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = event => {
this.setState({ current_value: JSON.parse(event.target.value)});
console.log(event.target.value);
};
handleSubmit = event => {
event.preventDefault();
const data = {
foo: 'bar',
'e0ea641b-3de4-4a76-857d-11da9352698a': {
current_value: this.state.current_value
}
};
console.log(data);
axios.post('http://my.server.url', data)
.then(response => {
const obj = response.data;
this.setState({ current_value: obj['e0ea641b-3de4-4a76-857d-11da9352698a'].current_value });
console.log(obj['e0ea641b-3de4-4a76-857d-11da9352698a'].current_value);
})
.catch((error) => {
console.log(error);
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>Input Number:
<input type="number" name="current_value" onChange={this.handleChange} />
</label>
<button type="submit">Post</button>
</form>
<div>
Output Number: { this.state.current_value }
</div>
</div>
);
}
}
export default Post;

Resources