How to build a React Web App with Ionic?

With the latest version of the Ionic framework, which lets you write mobile apps with web technologies, you can use React to build your mobile apps. Versions previous to 4 only has components for Angular, but now React support is complete. It has all the components for building apps to your liking.

Ionic 4 and later is a mobile app framework and a component library. You can build mobile apps, progressive web apps, and normal web apps. The component library can be used on its own. Of course, the hardware support remains in place for you to use if you wish. Right now, the React version of Ionic can only be used to build web apps. Only the Angular version can be built into a mobile app.

The full reference for the React version of Ionic is at https://ionicframework.com/docs.

In this article, we will build a React web app with Ionic that does currency conversion. The home page will display the list of the latest exchange rates and another page will have a form to convert the currencies of your choice. Tp get the data we use the Foreign exchange rates API, located at https://exchangeratesapi.io/ to get the exchange rates, and we use the Open Exchange Rates API, located at https://openexchangerates.org/, to get the list of currencies.

To start, we will start by installing the Ionic CLI. We run:

npm install -g ionic@latest

to install the latest version to your computer. Next we run:

ionic start currency-converter --type=react

then select the Sidenav option to create an Ionic project with a left side bar.

Next we install our own packages. We need Axios to make HTTP requests and MobX for state management. Run npm i axios mobx mobx-react in our project folder to install them.

Now we are ready to create some pages. In the pages folder, create ConvertCurrencyPage.jsx and add:

import {
  IonButtons,
  IonContent,
  IonHeader,
  IonItem,
  IonList,
  IonMenuButton,
  IonPage,
  IonTitle,
  IonToolbar,
  IonInput,
  IonLabel,
  IonSelect,
  IonSelectOption,
  IonButton
} from "@ionic/react";import React from "react";
import { observer } from "mobx-react";
import { getExchangeRates } from "../requests";const ConvertCurrencyPage = ({ currenciesStore }) => {
  const [fromCurrencies, setFromCurrencies] = React.useState({});
  const [toCurrencies, setToCurrencies] = React.useState({});
  const [values, setValues] = React.useState({ amount: 0 } as any);
  const [submitting, setSubmitting] = React.useState(false);
  const [toAmount, setToAmount] = React.useState(0);const convertCurrency = async () => {
    setSubmitting(true);
    if (values.amount <= 0 || !values.from || !values.to) {
      return;
    }
    const { data } = await getExchangeRates(values.from);
    const rate = data.rates[values.to];
    setToAmount(values.amount * rate);
  };React.useEffect(() => {
    const fromCurrencies = {};
    for (let key in currenciesStore.currencies) {
      if (key != values.to) {
        fromCurrencies[key] = currenciesStore.currencies[key];
      }
    }
    setFromCurrencies(fromCurrencies);const toCurrencies = {};
    for (let key in currenciesStore.currencies) {
      if (key != values.from) {
        toCurrencies[key] = currenciesStore.currencies[key];
      }
    }
    setToCurrencies(toCurrencies);
  }, [currenciesStore.currencies, values.from, values.to]);return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonButtons slot="start">
            <IonMenuButton />
          </IonButtons>
          <IonTitle>Convert Currency</IonTitle>
        </IonToolbar>
      </IonHeader><IonContent>
        <IonList lines="none">
          <IonItem>
            <IonInput
              type="number"
              value={values.amount}
              color={!values.amount && submitting ? "danger" : undefined}
              min="0"
              onIonChange={ev =>
                setValues({ ...values, amount: (ev.target as any).value })
              }
            ></IonInput>
          </IonItem><IonItem>
            <IonLabel>Currency to Convert From</IonLabel>
            <IonSelect
              placeholder="Select One"
              color={!values.from && submitting ? "danger" : undefined}
              onIonChange={ev =>
                setValues({ ...values, from: (ev.target as any).value })
              }
            >
              {Object.keys(fromCurrencies).map(key => {
                return (
                  <IonSelectOption value={key} key={key}>
                    {(fromCurrencies as any)[key]}
                  </IonSelectOption>
                );
              })}
            </IonSelect>
          </IonItem><IonItem>
            <IonLabel>Currency to Convert To</IonLabel>
            <IonSelect
              placeholder="Select One"
              color={!values.to && submitting ? "danger" : undefined}
              onIonChange={ev =>
                setValues({ ...values, to: (ev.target as any).value })
              }
            >
              {Object.keys(toCurrencies).map(key => {
                return (
                  <IonSelectOption value={key} key={key}>
                    {(toCurrencies as any)[key]}
                  </IonSelectOption>
                );
              })}
            </IonSelect>
          </IonItem><IonItem>
            <IonButton size="default" fill="solid" onClick={convertCurrency}>
              Convert
            </IonButton>
          </IonItem>{toAmount ? (
            <IonItem>
              {values.amount} {values.from} is {toAmount} {values.to}.
            </IonItem>
          ) : (
            undefined
          )}
        </IonList>
      </IonContent>
    </IonPage>
  );
};export default observer(ConvertCurrencyPage);

to add a form to convert currency from one to another. In this form, we filter by choices by excluding from the currency being converted to from the first drop down and exclude the currency being converted from in the second drop down. Also we have an ion-input for the amount being converted. We get the values of the currency from the currenciesStore , which is the MobX store that gets the list of currencies from. In the IonSelect components we set the onIonChange props to handler functions which set the drop down’s values. We also set the placeholder for all the inputs and selects. In the IonInput component, we do the same with the onIonChange handler. We show the right value by using the variables set in the values object during change events.

When the user clicks Convert, we run the convertCurrency function. We check if the values are set correctly before running the rest of the code. If that succeeds, then we run the getExchangeRates function imported from requests.js , then we set the final toAmount by multuplying the rate with the amount.

The useEffect callback is used for excluding the currency to convert from from the currency to convert to list and vice versa. The array in the second argument of useEffect specifies which values to watch for.

The observer function in the last line is for designating the component to watch the latest values from the MobX stores.

Next in Home.tsx , we replace the existing code with:

import {
  IonButtons,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonListHeader,
  IonMenuButton,
  IonPage,
  IonTitle,
  IonToolbar,
  IonSelect,
  IonSelectOption
} from "@ionic/react";
import React from "react";
import "./Home.css";
import { getExchangeRates } from "../requests";
import { CurrencyStore } from "../stores";
import { observer } from "mobx-react";const HomePage = ({ currencyStore, currenciesStore }) => {
  const [rates, setRates] = React.useState({});const getRates = async () => {
    const { data } = await getExchangeRates(
      (currencyStore as CurrencyStore).currency
    );
    setRates(data.rates);
  };React.useEffect(() => {
    getRates();
  }, [(currencyStore as CurrencyStore).currency]);
  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonButtons slot="start">
            <IonMenuButton />
          </IonButtons>
          <IonTitle>Home</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent>
        <IonList lines="none">
          <IonListHeader>Latest Exchange Rates</IonListHeader>          <IonItem>
            <IonLabel>Currency</IonLabel>
            <IonSelect
              placeholder="Select One"
              onIonChange={ev => {
                (currencyStore as CurrencyStore).setCurrency(
                  ev.target && (ev.target as any).value
                );
              }}
            >
              {Object.keys(currenciesStore.currencies).map(key => {
                return (
                  <IonSelectOption
                    value={key}
                    key={key}
                    selected={
                      (currencyStore as CurrencyStore).currency
                        ? key == (currencyStore as CurrencyStore).currency
                        : key == "AUD"
                    }
                  >
                    {(currenciesStore.currencies as any)[key]}
                  </IonSelectOption>
                );
              })}
            </IonSelect>
          </IonItem>
        </IonList>        <IonList lines="none">
          <IonListHeader>Exchange Rates</IonListHeader>
          {Object.keys(rates).map(key => {
            console.log(rates);
            return (
              <IonItem>
                {key}: {rates[key]}
              </IonItem>
            );
          })}
        </IonList>
      </IonContent>
    </IonPage>
  );
};export default observer(HomePage);

In this file, we display the exchange rates from an API. We get the currencies from the currenciesStore so that users can see exchange rates based on different currencies. The items are display in an IonList provided by Ionic.

The observer function in the last line is for designating the component to watch the latest values from the MobX stores.

Next in App.tsx , replace the following code with:

import React from "react";
import { Redirect, Route } from "react-router-dom";
import { IonApp, IonRouterOutlet, IonSplitPane } from "@ionic/react";
import { IonReactRouter } from "@ionic/react-router";
import { AppPage } from "./declarations";import Menu from "./components/Menu";
import Home from "./pages/Home";
import { home, cash } from "ionicons/icons";/* Core CSS required for Ionic components to work properly */
import "@ionic/react/css/core.css";/* Basic CSS for apps built with Ionic */
import "@ionic/react/css/normalize.css";
import "@ionic/react/css/structure.css";
import "@ionic/react/css/typography.css";/* Optional CSS utils that can be commented out */
import "@ionic/react/css/padding.css";
import "@ionic/react/css/float-elements.css";
import "@ionic/react/css/text-alignment.css";
import "@ionic/react/css/text-transformation.css";
import "@ionic/react/css/flex-utils.css";
import "@ionic/react/css/display.css";/* Theme variables */
import "./theme/variables.css";
import ConvertCurrencyPage from "./pages/ConvertCurrencyPage";
import { CurrencyStore, CurrenciesStore } from "./stores";
import { getCurrenciesList } from "./requests";const currencyStore = new CurrencyStore();
const currenciesStore = new CurrenciesStore();const appPages: AppPage[] = [
  {
    title: "Home",
    url: "/home",
    icon: home
  },
  {
    title: "Convert Currency",
    url: "/convertcurrency",
    icon: cash
  }
];const App: React.FC = () => {
  const [initialized, setInitialized] = React.useState(false);  const getCurrencies = async () => {
    const { data } = await getCurrenciesList();
    currenciesStore.setCurrencies(data);
    setInitialized(true);
  };React.useEffect(() => {
    if (!initialized) {
      getCurrencies();
    }
  });  return (
    <IonApp>
      <IonReactRouter>
        <IonSplitPane contentId="main">
          <Menu appPages={appPages} />
          <IonRouterOutlet id="main">
            <Route
              path="/home"
              render={() => (
                <Home
                  currencyStore={currencyStore}
                  currenciesStore={currenciesStore}
                />
              )}
              exact={true}
            />
            <Route
              path="/convertcurrency"
              render={() => (
                <ConvertCurrencyPage
                  currenciesStore={currenciesStore}
                />
              )}
              exact={true}
            />
            <Route exact path="/" render={() => <Redirect to="/home" />} />
          </IonRouterOutlet>
        </IonSplitPane>
      </IonReactRouter>
    </IonApp>
  );
};export default App;

We modified the routes so that we use the render prop instead of the component prop since we want to pass in our MobX stores into the components. The stores store the currently selected currency for the home page and the list of currencies for both pages.

We get the list of currencies here for both components and set the data in store for both to retrieve by setting it in store.

Next create requests.ts in the src folder and add:

const axios = require('axios');
const APIURL = 'https://api.exchangeratesapi.io';
const OPEN_EXCHANGE_RATES_URL = 'http://openexchangerates.org/api/currencies.json';

export const getExchangeRates = (baseCurrency: string) => axios.get(`${APIURL}/latest?base=${baseCurrency}`)

export const getCurrenciesList = () => axios.get(OPEN_EXCHANGE_RATES_URL)

These are the code for making the HTTP requests to get the currencies and exchange rates.

Next create store.ts to and add:

import { observable, action } from "mobx";class CurrencyStore {
    @observable currency: string = 'AUD';    @action setCurrency(currency: string) {
        this.currency = currency;
    }
}class CurrenciesStore {
    @observable currencies = {};    @action setCurrencies(currencies) {
        this.currencies = currencies;
    }
}export { CurrencyStore, CurrenciesStore };

The CurrencyStore is for storing the selected currency in the home page to show the exchange rates based on the selected currency. currency is the value observed by the home page and setCurrency sets the currency value. Similarly, CurrenciesStore stores a list of currencies retrieved in App.tsx where setCurrencies is called.

Next in tsconfig.json , we replace the existing code with:

{
  "compilerOptions": {
      "experimentalDecorators": true,
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",
    "noImplicitAny": false
  },
  "include": [
    "src"
  ]
}

to noImplicitAny and set it to true .

Finally, in index.html , replace the existing code with:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Currency Converter</title><base href="/" /><meta
      name="viewport"
      content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
    />
    <meta name="format-detection" content="telephone=no" />
    <meta name="msapplication-tap-highlight" content="no" /><link
      rel="shortcut icon"
      type="image/png"
      href="%PUBLIC_URL%/assets/icon/favicon.png"
    /><!-- add to homescreen for ios -->
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-title" content="Ionic App" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
  </head><body>
    <div id="root"></div>
  </body>
</html>

to change the title.

After all the hard work is done, we get:


#React #Ionic #webdev #webapp #javascript

How to build a React Web App with Ionic?
49.70 GEEK