Use Axios to load weather forecast to React Native CLI application

U

You will create a Weather Forecast application with React Native CLI. Weather Forecast will be loaded with Axios from the OpenWeather. Application UI will be created with NativeBase components. Created project is tested with a emulator and a real device. To test with emulators you will need to install Android Studio or Xcode.

Purpose and learning process

In this tutorial you will learn how to display a weather forecast in React Native CLI.

  • using and rendering build-in components
  • using and rendering 3th party UI components
  • understand state and props
  • event handling
  • passing data between components
  • using styles
  • load and parse JSON data

Example screenshots and video

Application will load weather forecast data from OpenWeather and display weather forecast with Card components.

Look example video here: https://youtu.be/f5cHD1nhWsQ.

Weather Forecast data

Find a free weather forecast provider, which offers XML or JSON data. You can use for example https://openweathermap.org/. Get your own API key, you will need it later in this exercise.

NativeBase

NativeBase is a free and open source UI component library for React Native to build native mobile apps for iOS and Android platforms. You can find NativeBase website here: NativeBase.

Axios

In this exercise you can load JSON data using Axios.

App requirements

Your app must meet the following requirements:

  • Create a React Native CLI app
  • Use Axios to load Weather Forecast data
  • App displays current weather forecast
  • Cities can be added and removed
  • UI is created with NativeBase or some other 3th party UI library for React Native
  • Application saves cities data, so cities are available when app is launched

Create project and install npms

Create WeatherApp React Native CLI app and install needed npm packages.

npx react-native init WeatherApp
npm install axios --save
npm install native-base --save

Create UI

NativeBase is made from effective building blocks referred to as components. The Components are constructed in pure React Native platform along with some JavaScript functionality with rich set of customisable properties. These components allow you to quickly build the perfect interface.

Read documentation and implement UI with Header and Button components.

<Container>
  <Header>
    <Left/>
    <Body>
      <Title>Weather App</Title>
    </Body>
    <Right>
      <Button>
        <Text>Add</Text>
      </Button>
    </Right>
  </Header>
</Container>

Header component should contain Title and Button components.

Add a new city

Create UI where user can add/give a new city name. Here you can use (for example) React-Native dialog, which display a dialog – or create own UI for that purpose.

Install React-Native dialog npm:

npm install react-native-dialog --save

Add a Dialog for rendering (before closing Container component). Input component will be used to get a new city name. onChangeText event will use React Hooks to change cityName state (look codes later).

<Dialog.Container visible={modalVisible}>
  <Dialog.Title>Add a new city</Dialog.Title>
  <Form>
    <Item>
      <Input onChangeText={ (text) => setCityName(text)} placeholder="cityname"/>
    </Item>
  </Form>
  <Dialog.Button label="Cancel" onPress={cancelCity} />
  <Dialog.Button label="Add" onPress={addCity} />
</Dialog.Container>

A dialog should be opened, when add Button component is pressed in header. Here you can use for example React Hooks to change dialog visible/invisible state. Hooks are new in React and you should give time for learning it.

Add React Hooks for modalVisiblecityName and citiesstates.

const [modalVisible, setModalVisible] = useState(false); 
const [cityName, setCityName] = useState(""); 
const [cities, setCities] = useState([]);

Add a onPress event handling to Button component inside a Header component.

<Text onPress={openDialog}>Add</Text>

Add openDialog function, which will modify modalVisible state (dialog is visible/invisible).

const openDialog = () => {
  setModalVisible(true);
}

Dialog’s ADD button will call addCity function, which will add city to cities array and hides a dialog.

const addCity = () => {
  setCities( [...cities,{id:cities.length, name:cityName}]);
  setModalVisible(false);
}

Code cancelCity function just to set modalVisible to false with setModalVisible function.

Cities array can be rendered after Header component. Use map function to loop through cities array and generate Text component to show a city name.

{cities.map(city => (
  <Text key={city.id}>{city.name}</Text>
))}

Test

Save and test your app. It should display city names now.

Use own made component with cities

The purpose of this exercise is to use Card component to show city weather forecast. Check and learn NativeBase – Card component.

Create own WeatherForecast component. Component will get city name via params.

const WeatherForecast = (params) => {
  const city = params.city;
  return (
    <Card>
      <CardItem>
        <Body>
          <Text>
             {city}
          </Text>
        </Body>
      </CardItem>
    </Card>
  );
}

Use above WeatherForecast component in main App.

<ScrollView>
  {cities.map(city => (
    <WeatherForecast key={city.id} city={city.name} />
  ))}
</ScrollView>

Test

Save and test your app. It should display city name inside a Card component.

Load a weather forecast

You can access current weather data for any location on Earth including over 200,000 cities with current weather data @ OpenWeather. Check and learn the API calls to get weather forecast by city name.

Weather forecast can be now loaded and displayed inside a WeatherForecast component. Use React hooks for axios to load weather forecast.

Install npm:

npm install axios-hooks

Import axios-hooks and load weather forecast inside a WeatherForecast component.

const WeatherForecast = (params) => {
  const city = params.city;
  const API_KEY = 'your_api_key_here';
  const URL = 'https://api.openweathermap.org/data/2.5/weather?q=';

  const [{ data, loading, error }, refetch] = useAxios(
    URL+city+'&appid='+API_KEY
  )

  if (loading) return <Text>Loading...</Text>;
  if (error) return <Text>Error!</Text>;

  // just for testing
  console.log(data);

  return (
    <Card>
      <CardItem>
        <Body>
          <Text>
             {city}
          </Text>
        </Body>
      </CardItem>
    </Card>
  );
}

Test

Save and test your app. Check your Metro builder’s log data, it should display weather forecast data in JSON format.

{
  "base": "stations", 
  "clouds": {"all": 75}, 
  "cod": 200, 
  "coord": {"lat": 61.5, "lon": 23.75}, 
  "dt": 1582045660, 
  "id": 634964, 
  "main": {"feels_like": 270.86, "humidity": 75, "pressure": 988, "temp": 277.8, "temp_max": 278.15, "temp_min": 277.15}, 
  "name": "Tampere", 
  ...

Learn returned JSON structure or read API carefully.

Show a weather forecast

Select some of the weather forecast data and show it inside a WeatherForecast component.

For example – main and temp. Be innovative with UI!

<Text>Main: {data.weather[0].main}</Text>
<Text>Temp: {data.main.temp} °C</Text>

Test

Save and test your app. It should display weather forecast inside a Card component.

Refresh weather forecast

Add a refresh “Button” to your app and refresh weather forecast.

You can use already defined React refetch hooks. It will automatically use Axios to fetch a new forecast.

const refreshForecast = () => {
  refetch();
}

Remove city

Add a remove “Button” to your app and delete city from cities state.

You should add id and deleteCity params to WeatherForecast component rendering in main App.

{!modalVisible && cities.map(function(city,index){
  return (
    <WeatherForecast 
      key={index} 
      city={city.name} 
      id={city.id} 
      deleteCity={deleteCity} />
  )
})

Add and call below deleteCity function from your “Button” in WeatherForecast component. City id will be send to main App’s deleteCity function.

const deleteCity = () => {
  params.deleteCity(id);
}

Add below deleteCity function to main App. Array’s filter function will be used to filter out city with exact id and a new state for cities will be set with setCities hook.

const deleteCity = (id) => {
  let filteredArray = cities.filter(city => city.id !== id);
  setCities(filteredArray);
}

Test

Save and test your app. It should work with add/remove city and refresh weather forecast.

Save and load cities to/from local storage

A final step is to save cities to device’s local storage. You can use React Native Async Storage, which is a an asynchronous, unencrypted, persistent, key-value storage system for React Native.

Read documentation and install it to your project.

You should stringify cities, before save it to local storage.

const storeData = async () => {
  try {
    await AsyncStorage.setItem('@cities', JSON.stringify(cities));
  } catch (e) {
    // saving error
    console.log("Cities saving error!");
  }
}

And remember parse it back to array and use setCities hook to set cities.

const getData = async () => {
  try {
    const value = await AsyncStorage.getItem('@cities')
    if(value !== null) {
      setCities(JSON.parse(value));
    }
  } catch(e) {
    console.log("Cities loading error!");
  }
}

Above functions should be called, when cities need to be saved and loaded to/from local storage.

React Native’s useEffect hook can be used here in both save and read data. Check documentation here: Using the Effect Hook.

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.

Now saved cities are loaded only once, when app starts, so pass [] empty array in second argument and call above getData to load saved cities from local storage.

// load cities when app starts
useEffect(() => {
  getData();
},[]);  

Modified cities can be detected with useEffect also. Pass [cities] array in second argument. storeData will be called every time, when cities state changes (cities are added or removed).

// save cities if cities state changes
useEffect(() => {
  storeData();
},[cities]); 

Test

Test your application in emulator/simulator. It should work between launching times. 

Add comment

Pasi Manninen

Pasi Manninen

Pasi is a mobile and web application developer. Currently working as a senior lecturer in JAMK University of Applied Sciences. Pasi has programming experience over many decades and has taught dozens of courses since the 90's.

Recent Posts

Follow Me