Fabian
Fabian

Reputation: 97

Getting props from parent component state to render data

I am building a weather app. The behavior would be to have a Button in my main menu. This Button should display the current weather. When clicking this button it should display a card with all the weather informations. This is pretty similar to Momentum https://momentumdash.com/

I could successfully create my Weather Card displaying the current Weather and also the forecast.(/home/starke/Screenshot from 2019-04-06 14-57-35.png

My issue is I do not know how to display the weather in my button before I click on it to display weather. Not sure how to access my data and render it.

My SideMenu component displaying the Menu

export default class SideMenu extends React.Component {
    constructor(props) {
        super(props);
    }

    changeView(e, view) {
      e.preventDefault();
      this.props.changeView(view);
    }

    render() {
      const { cityName } = this.props;
            return (<Menu>
              <Button onClick={(e) => this.changeView(e, "weather")}>
              </Button>
              <Button onClick={(e) => this.changeView(e, "todo")}>
                ToDo
              </Button>
              <Button onClick={(e) => this.changeView(e, "pomodoro")}>
                Pomo
              </Button>
              <Button onClick={(e) => this.changeView(e, "picture")}>
                Info
              </Button>
            </Menu>);
    }
}


The Weather Card component where I get the data from the API and render it

class WeatherCard extends Component {
  constructor(props) {
    super(props);

    this.state = {
      temperature: "",
      latitude: "",
      longitude: "",
      summary: "",
      cityName: "",
      numForecastDays: 5,
      isLoading: false
    };
  }

  componentDidMount() {
    this.getLocation();
  }

  // Use of APIXU API with latitude and longitude query
  getWeather() {
    const { latitude, longitude, numForecastDays } = this.state;
    const URL = `https://api.apixu.com/v1/forecast.json?key=${KEY}&q=${latitude},${longitude}&days=${numForecastDays}`;
    axios
      .get(URL)
      .then(res => {
        const data = res.data;

        this.setState({
          cityName: data.location.name + ", " + data.location.region,
          summary: data.current.condition.text,
          temperature: data.current.temp_c,
          forecastDays: data.forecast.forecastday,
          iconURL: data.current.condition.icon
        });
      })
      .catch(err => {
        if (err) console.log(err);
      });
  }

  // function using current longitude and latitude of user
  // This requires authorization from user // Could be changed using IP adress instead, but would be less precise
  getLocation() {
    navigator.geolocation.getCurrentPosition(
      position => {
        this.setState(
          prevState => ({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude
          }),
          () => {
            this.getWeather();
          }
        );
      },
      error => this.setState({ forecast: error.message }),
      { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
    );
  }
  render() {
    const {
      summary,
      temperature,
      cityName,
      iconURL,
      forecastDays,
      isLoading
    } = this.state;
    return (
      <div>
        {isLoading && (
          <div>
            <Spinner />
            <LoaderText>Loading....</LoaderText>
          </div>
        )}
        {!isLoading && (
          <Wrapper>
            <CurrentWeather
              cityName={cityName}
              summary={summary}
              temperature={temperature}
              icon={iconURL}
            />
            <Forecast forecastDays={forecastDays} />
          </Wrapper>
        )}
      </div>
    );
  }
}

export default WeatherCard;

Upvotes: 0

Views: 89

Answers (1)

Abdelkarim EL AMEL
Abdelkarim EL AMEL

Reputation: 1533

You can control the display of you widget using the state.

You can pass a click handler to your sidemenu as a prop, once you click on an item you emit the click event to the parent component (with some payload if you want).

The parent component will have a handler method which is responsible for displaying your widget.

I've made some adjustments into you index.js and SideMenu.js files.

index.js

import React, { Component } from 'react';
import { render } from 'react-dom';
import WeatherCard from './Weather';
import SideMenu from './SideMenu';

class App extends Component {
  constructor() {
    super();
    this.state = {
      showWeather: false
    }
  }

  handleItemClick = (item) => {
    if (item === 'weather') {
       this.setState({
        showWeather: true
      });
    }
  } 
  render() {
    return (
      <div>
        <SideMenu onItemClick={this.handleItemClick} />
        {this.state.showWeather ? <WeatherCard /> : null}
      </div>
    );
  }
}

render(<App />, document.getElementById('root'));

SideMenu.js

export default class SideMenu extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    const { cityName } = this.props;
    return (
      <Menu>
        <Button onClick={() => this.props.onItemClick('weather')}>
          Open Weather Widget
        </Button>
      </Menu>
    );
  }
}

here is a fully working stacklitz with the adjustments mentioned above, hope that this will help.

If you want a data to be accessible by all the components, then you have these options:

React Context

Redux or MobX which are state management libraries.

Upvotes: 1

Related Questions