blujay
blujay

Reputation: 19

Can't call class functions from main, "is not a function" error

I'm attempting to learn react by creating a simple weather app which uses api requests to get the data. The api requires a location key which can be obtained through another function. this means i have to return and then pass the key along to the next function but I seem to be having some trouble doing this!

TypeError: _LocationKey__WEBPACK_IMPORTED_MODULE_3__.default.setLocation is not a function" is the error i am given when I attempt to run.

I'm assuming I made an error with how i am calling the functions as well as how the actual classes are structured. Any help would be appreciated!

//The main app
function App() {

  //runs function to set api location key
  new LocationKey();
  LocationKey.setLocation('winnipeg');

  //uses get location key to set weatherData
  new Weather();
  Weather.getWeatherData(LocationKey.getLocation());

  //displays location and weather information 
  return (
    <div className="body">
          <LocationKey/>
          <Weather/>
    </div>
  );
}

//The location key class
export class LocationKey extends Component{

  state = {
    locations: []
  }

  setLocation(name) {
    axios.get('http://dataservice.accuweather.com/locations/v1/cities/search?apikey=oqAor7Al7Fkcj7AudulUkk5WGoySmEu7&q=' + name + '&language=en&details=false')
      .then(res => {
        const locations = res.data;
        this.setState({ locations });
      })
  }

  getLocation(){
      return this.state.locations[0].Key;
  }


  render() {
    return (
      <ul>
        { this.state.locations.slice(0, 1).map(location => <li>{location.EnglishName}</li>)}
      </ul>
    )
  }
}
export default LocationKey

//Weather Class
export class Weather extends Component{

  state = {
    weatherData: []
  }

  //issues command to find weather data from api using location key
  getWeatherData(location) {
    axios.get('http://dataservice.accuweather.com/forecasts/v1/daily/1day/' + location + '?apikey=oqAor7Al7Fkcj7AudulUkk5WGoySmEu7&language=en&details=false&metric=true%20HTTP/1.1')
      .then(res => {
        const weatherData = res.data;
        this.setState({ weatherData });
      })
  }

  render() {
    return (
      <ul>
        { this.state.weatherData.slice(0, 2).map(weather => <li>{weather.Headline.Text}</li>)}
        { this.state.weatherData.slice(0, 2).map(weather => <li>{weather.Headline.Category}</li>)}
        { this.state.weatherData.slice(0, 2).map(weather => <li>{weather.DailyForecasts.Maximum.Value}</li>)}
        { this.state.weatherData.slice(0, 2).map(weather => <li>{weather.DailyForecasts.Minimum.Value}</li>)}
      </ul>
    )
  }
}
export default Weather

Upvotes: 2

Views: 748

Answers (1)

Derek Pollard
Derek Pollard

Reputation: 7165

The problem

The issue lies with this line:

LocationKey.setLocation('winnipeg');

You instantiate a new instance of LocationKey, but seeing as LocationKey isn't a singleton and setLocation is not a static method, you're trying to call an instance method in a static way.

Another potential issues is this line:

Weather.getWeatherData(LocationKey.getLocation());

As I said above, you instantiate a new instance of Weather, but since it isn't doing anything and isn't assigned to anything, it gets immediately thrown away, so the following line has no effect on it.

The solution

I would suggest making App its own component and having it contain the state for the locations, then passing the locations array to each of the components.

class App extends React.Component {

  state = {
    locations: ['winnipeg'],
    locationInput: ''
  };

  render() {
    return ( 
      <div>
        <input value={this.state.locationInput} onChange={e => this.setState({ locationInput: e.target.value })} />
        <button onClick={this.handleAddLocation}>add to locations</button>
        <Weather locations={this.state.locations} />
        <LocationKey locations={this.state.locations} />
      </div>
    )
  }
  
  handleAddLocation = () => {
    if (this.state.locationInput === '') return;
    this.setState({
      locationInput: '',
      locations: [...this.state.locations, this.state.locationInput]
    });
  };
}

class Weather extends React.Component {
  static defaultProps = {
    locations: []
  }

  render () {
    return (
      <div>
        <h2>Weather Component</h2>
        <h3>Locations provided:</h3>
        <span>{this.props.locations.join(', ')}</span>
      </div>
    );
  }
}


class LocationKey extends React.Component {
  static defaultProps = {
    locations: []
  }

  render () {
    return (
      <div>
        <h2>LocationKey Component</h2>
        <h3>Locations provided:</h3>
        <span>{this.props.locations.join(', ')}</span>
      </div>
    );
  }
}

ReactDOM.render( < App / > ,
  document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app"></div>

In the above snippet, I set out to show you how props can be used and provided to child components.

I hope this can help and clarify some of this issues you've been having

Upvotes: 1

Related Questions