问题
This is my first try at a simple React application. Working with the Openweather API and AXIOS. I took Stephen Grider's course on Udemy and I'm trying to create something on my own now, but I still have problems when passing data between components. I have a SearchBar component and I'd love to be able to pass the input value to the parent component's state, so I can then update it with each search and render it into the DOM. However, I keep running into errors. I tried passing a function as a prop to my SearchBar component but I'm getting errors:
setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the App component.
citySearch is not defined
This is confusing to me, as I tried to copy the exact steps from the course, where it seems to be working just fine. But again, I'm very new to this so it is probably just me making some sort of a rookie mistake. Any tips would be much appreciated.
Check my code below:
App.js
import React, { Component } from 'react';
import './App.css';
//Libraries
import axios from 'axios';
//Components
import SearchBar from './Components/search-bar';
class App extends Component {
constructor(props){
super(props);
this.state = {
city: 'London',
country: 'uk',
temperature: 0,
humidity: 0,
pressure: 0
}
//Axios call
let city = this.state.city;
let country = this.state.country;
axios
.get(`http://api.openweathermap.org/data/2.5/weather?APPID=${API_KEY}&q=${city},${country}`)
.then(function(response) {
this.setState({
city: response.data.name,
country: response.data.name,
temperature: response.data.main.temp,
humidity: response.data.main.humidity,
pressure: response.data.main.pressure
});
}.bind(this))
.catch(function(error) {
console.log(error);
});
this.citySearch('London');
}
citySearch(city){
this.setState({city})
}
render() {
return (
<div className="container">
<h1 className="display-1 text-center">Weather App</h1>
<SearchBar onSearchTermChange={citySearch} />
</div>
);
}
}
export default App;
SearchBar component:
import React, { Component } from 'react';
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = {
city: ""
};
}
render() {
return (
<input
value={this.state.city}
onChange={this.onHandleChange}
className="form-control mt-3"
placeholder="Enter a city name..."
type="text"
/>
);
}
onHandleChange(city) {
this.setState({ city });
this.props.onSearchTermChange(city);
}
}
export default SearchBar;
回答1:
setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the App component.
This is because of your axios
call inside the constructor. Put your axios call in componentDidMount
should resolve it
citySearch is not defined
It is because React cannot find citySearch
function. You should change
<SearchBar onSearchTermChange={citySearch} />
to
<SearchBar onSearchTermChange={this.citySearch} />
In order to use citySearch
this way, you should also bind citySearch
in your constructor
In summary:
import React, { Component } from 'react';
import './App.css';
//Libraries
import axios from 'axios';
//Components
import SearchBar from './Components/search-bar';
class App extends Component {
constructor(props){
super(props);
this.state = {
city: 'London',
country: 'uk',
temperature: 0,
humidity: 0,
pressure: 0
}
this.citySearch = this.citySearch.bind(this)
}
componentDidMount() {
//Axios call
let city = this.state.city;
let country = this.state.country;
axios
.get(`http://api.openweathermap.org/data/2.5/weather?APPID=${API_KEY}&q=${city},${country}`)
.then(function(response) {
this.setState({
city: response.data.name,
country: response.data.name,
temperature: response.data.main.temp,
humidity: response.data.main.humidity,
pressure: response.data.main.pressure
});
}.bind(this))
.catch(function(error) {
console.log(error);
});
}
citySearch(city){
this.setState({city})
}
render() {
return (
<div className="container">
<h1 className="display-1 text-center">Weather App</h1>
<SearchBar onSearchTermChange={this.citySearch} />
</div>
);
}
}
export default App;
Don't call setState
in your constructor, you can just initialize your state like your did. So the original setState
in your constructor should be deleted.
UPDATE
To search again everytime you call citySearch
.
import React, { Component } from 'react';
import './App.css';
//Libraries
import axios from 'axios';
//Components
import SearchBar from './Components/search-bar';
class App extends Component {
constructor(props){
super(props);
this.state = {
city: 'London',
country: 'uk',
temperature: 0,
humidity: 0,
pressure: 0
}
this.citySearch = this.citySearch.bind(this)
}
componentDidMount() {
axioSearch();
}
axioSearch(city) {
let city = city || this.state.city;
let country = this.state.country;
axios
.get(`http://api.openweathermap.org/data/2.5/weather?APPID=${API_KEY}&q=${city},${country}`)
.then(function(response) {
this.setState({
city: response.data.name,
country: response.data.name,
temperature: response.data.main.temp,
humidity: response.data.main.humidity,
pressure: response.data.main.pressure
});
}.bind(this))
.catch(function(error) {
console.log(error);
});
}
citySearch(city){
this.axioSearch(city);
}
render() {
return (
<div className="container">
<h1 className="display-1 text-center">Weather App</h1>
<SearchBar onSearchTermChange={this.citySearch} />
</div>
);
}
}
export default App;
回答2:
You just setState in constructor. If you want to make a call you can put it in componentWillMount()
or componentDidMount()
import React, { Component } from 'react';
import './App.css';
//Libraries
import axios from 'axios';
//Components
import SearchBar from './Components/search-bar';
class App extends Component {
constructor(props){
super(props);
this.state = {
city: 'London',
country: 'uk',
temperature: 0,
humidity: 0,
pressure: 0
}
}
citySearch(city){
this.setState({city})
}
componentWillMount(){
//Axios call
let city = this.state.city;
let country = this.state.country;
axios
.get(`http://api.openweathermap.org/data/2.5/weather?APPID=${API_KEY}&q=${city},${country}`)
.then(function(response) {
this.setState({
city: response.data.name,
country: response.data.name,
temperature: response.data.main.temp,
humidity: response.data.main.humidity,
pressure: response.data.main.pressure
});
}.bind(this))
.catch(function(error) {
console.log(error);
});
this.citySearch('London');
}
render() {
return (
<div className="container">
<h1 className="display-1 text-center">Weather App</h1>
<SearchBar onSearchTermChange={citySearch} />
</div>
);
}
}
export default App;
回答3:
First, you should not make your axios calls in the constructor. The component is not yet mounted at this point. Do this in a componentDidMount to ensure that the component is already mounted.
Secodly, you did not bind citySearch to the App class. So in the SearchBar component, it does not know that the citySearch Method should be called from the App class. It is advisable to do this binding in the App class constructor for optimization reasons.
Lastly, I will advise you to write React in a more functional way leveraging state management frameworks like Redux or Flux
The code below should work
import React, { Component } from 'react';
import './App.css';
//Libraries
import axios from 'axios';
//Components
import SearchBar from './Components/search-bar';
class App extends Component {
constructor(props){
super(props);
this.state = {
city: 'London',
country: 'uk',
temperature: 0,
humidity: 0,
pressure: 0
}
this.citySearch = this.citySearch.bind(this);
this.citySearch('London');
}
citySearch(city){
this.setState({city})
}
componentDidMount() {
//Axios call
let {city, country} = this.state;
axios
.get(`http://api.openweathermap.org/data/2.5/weather?APPID=${API_KEY}&q=${city},${country}`)
.then(function(response) {
this.setState({
city: response.data.name,
country: response.data.name,
temperature: response.data.main.temp,
humidity: response.data.main.humidity,
pressure: response.data.main.pressure
});
}.bind(this))
.catch(function(error) {
console.log(error);
});
}
render() {
return (
<div className="container">
<h1 className="display-1 text-center">Weather App</h1>
<SearchBar onSearchTermChange={citySearch} />
</div>
);
}
}
export default App;
For the searchBar component, you didn't bind onHandleChange in the SearchBar component. This will throw errors. You should do this in the searchBar constructor
constructor() {
...
this.onHandleChange = this.onHandleChange.bind(this) //Very important you do this
}
回答4:
To pass data from child components to the parent component you have to use a callback method.
Check this out. (About how to pass data from parent to child and child to parent).
I know that I'm not touching your code here (I'm sorry), but if you're interested in a different approach this works.
https://medium.com/@ruthmpardee/passing-data-between-react-components-103ad82ebd17
来源:https://stackoverflow.com/questions/46091409/react-passing-data-between-components