React - passing data between components

99封情书 提交于 2019-12-13 03:52:08

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!