Walkthrough: External Data & Component Lifecycle π‘
Solution
Rewire app
1. In your index.js
file, remove the imported JSON file, and remove the props you are passing into <App />
2. In <App />
component, replace the forecasts
and location
props with new state variables of the same name
Use the useState()
hook to create forecasts
and location
. Set their values to an empty array for forecasts
, and an object with city
and country
properties with a value of an empty string for location
.
Make sure all references to forecasts
and location
in this component are correct.
3. Set selectedDate
to 0 - this is just a default/initial value
The app no longer has any forecasts when it loads (we have to wait for the data to load from the API), so our old approach will not work - the forecast we were referring to will no longer exists.
4. Add conditional rendering for the <ForecastDetails />
component
One other thing we need to fix is in the usage of the <ForecastDetails />
component.
Our <ForecastDetails />
component is expecting a selected forecast to be passed in. We are selecting that forecast using the following code in <App />
component:
Array.find()
will return either a matching item, or undefined
. When our app initially mounts and is rendered, we set the forecasts
array to be empty - this means that find()
will always return undefined
(since there is nothing to find), and so we will always be passing undefined
into our <ForecastSummary />
component. Looking back at the error messages, can you see that is what it is telling us?
To fix this, we can use conditional rendering. This allows us to use if statements, ternaries and logical operators to render one thing or another (or nothing at all), based on the state of the component.
In this case, we only want to render the <ForecastDetails />
if there is a forecast
with the selectedDate
.
In your <App />
components JSX block change <ForecastDetails forecast={selectedForecast} />
to the following:
Because we are using actual JavaScript, not just JSX, we have to wrap this block in curly braces. We are then leveraging the truthiness/falsiness of selectedForecast
in combination with the &&
operator to conditionally render the <ForecastDetails />
component. If selectedForecast
is undefined
, then the statement will evaluate to false
and React will not render anything.
If selectedForecast
is a truthy value (which it will be if it is a forecast object), then the statement will evaluate to true and return its right hand side operand. Thus the <ForecastDetails />
component will be rendered.
Fetch Weather App API
If you've got this far, then you've successfully rewired your app so that instead of reading data from the static JSON file that was passed in from outside as props, the data is now completely contained within the local state of the <App />
component. This means that <App />
is ready to be in charge of its own data once it receives it from the API.
5. Use npm to install the axios
library, and import this into your <App />
component
No magic here, you know how to do it π.
6. Create getForecast()
method that will make a call to forecast API with axios
.
This method meant to be very simple, it should only make a call to APIs and console.log()
, so you can verify you get expected data.
7. Call getForecast()
in useEffect()
hook.
Add a useEffect()
hook to your <App />
component, the same way you did it for useState()
.
In the useEffect()
, use the axios
request getForecast()
you just created to make an HTTP request for some weather data from the above API, at the right moment of component lifecycle.
8. Set the values of forecasts
, location
, and selectedDate
appropriately within the getForecast()
request
Now when <App />
receives some data, you can set actual values for forecasts
and location
state variables. Use the setForecasts()
, setLocation()
, and setSelectedDate()
methods to so. setSelectedDate()
should set the selectedDate
to the date of the first forecast, just as we did before with the static data.
Tidy up
Our app should now work as before, but our work is not finished yet.
9. Check browser console for any warnings and fix them
Most likely console will display an error for an icon we pass to <ForecastSummaries />
. The reason is the strings, which can be coerced to numbers, are getting coerced to that type when we do our request for forecast data. The way to fix it is to change the expected type in props validation to a number in the first component that receives the prop, but convert the number back to a string when passing it down to the child component (<ForecastSummary />
). You can do this in <ForecastSummaries />
:
- In the JSX block change
icon={forecast.icon}
toicon={forecast.icon.toString()}
.
We do this because we still want to pass the icon
value as a string because the 3rd party component <WeatherIcon />
(which we use in <ForecastSummary />
) expects a string and not a number.
10. Move getForecast()
to a separate src/requests/getForecast.js
file
This will make your component cleaner, easier to read and more reusable, as well as the getForecast()
request itself. This way our app will follow Separation of Concerns (SoC) and Single Responsibility Principle (SRP) principles. Your new file should look like this:
Make sure you're passing all set**()
methods to getForecast()
in <App />
!
If you managed all that, then congratulations! You should see the live weather data being displayed in your app instead of static data from the JSON file.