blake
blake

Reputation: 501

React - how to set state with the data from a resolved promise

I'm struggling to get my data from a fetch request into the state of my container

My fetch request is stored in api.js and looks like this - it retrieves the key from a constant which is fine:-

import { openWeatherKey } from './constants';

const getWeather = async() => {
    const base = "https://api.openweathermap.org/data/2.5/onecall";
    const query = `?lat=52.6&lon=-2.2&exclude=hourly,daily&appid=${openWeatherKey}`;

    const response = await fetch(base + query);
    const data = await response.json();

    return data;
}

export { getWeather };

My container looks like this:-

import React, { Component } from "react";
import './weather.css';
import { getWeather } from './api';

class Spy extends Component {

    constructor() {
        super()
        this.state = {test(){return "this is a test"}}
    }

    render() {
        return (
            <div id="spy-weather" className="app-border">
                <h3 className="spy-name">Weather at { this.props.location } {this.state.test()}</h3> 
            </div>
        )
    }
}

(() => {
    getWeather().then(data => {
        console.log(data);  
    })
})();  

export { Spy as Weather };

I have an IIFE which makes the request and prints the results to the console. You can see that between the class declaration and the export statement above.

Here are the results from the console - the request works fine

{lat: 52.6, lon: -2.2, timezone: "Europe/London", timezone_offset: 3600, current: {…}}
current: {dt: 1594401262, sunrise: 1594353486, sunset: 1594412995, temp: 289.05, feels_like: 286.49, …}
lat: 52.6
lon: -2.2
timezone: "Europe/London"
timezone_offset: 3600
__proto__: Object

What I can't manage to do is set the state with the data from the resolved promise. I've tried various things, including some solutions I've seen which didn't work.

How do I place and run the function within the container and then update state with the data?

I'm pretty new to React as you can probably tell.

With sincere thanks,

Phil

Upvotes: 1

Views: 3715

Answers (2)

Yousaf
Yousaf

Reputation: 29314

In class based components, lifecycle method known as componentDidMount is used to do something after component has mounted. In your case, move the code in IIFE in the componentDidMount method.

Make a property in state object which will hold the weather data. Optionally, you can also make a property in state object to hold any error message that might occur during the fetching of data from the API.

this.state = {
   weatherData: null,
   error: ''
};

and then call getWeather() function from componentDidMount() lifecycle method

componentDidMount() {
    getWeather()
      .then(data => {
        this.setState({ weatherData: data });
      })
      .catch(error => this.setState({ error: error.message }));
}

In functional components, useEffect hook is used to perform any side-effect like fetching data from an API. State in functional components is saved using useState hook.

If you use a functional component, then your code will look like this:

const [weatherData, setWeatherData] = useState(null);
const [error, setError] = useState(null);

useEffect(() => {
    getWeather()
      .then(data => {
          setWeatherData(data);
      })
      .catch(error => setError(error.message));
}, []);

Upvotes: 3

Nijat Aliyev
Nijat Aliyev

Reputation: 894

this.state = {test(){return "this is a test"}}

This is invalid structure for state managment, right way

getWeather().then(data => {
        console.log(data);
        this.setState({ weatherData: data });
})

state structure too

state = {
  someProperty: value,
  someArray: [],
  weatherData: {}
}

Upvotes: 0

Related Questions