How to build a React Web App with Ionic?

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. In this React tutorial, you'll learn 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 [email protected]

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:


Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

What’s new in HTML6

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

React Hooks: useState and useEffect in React.js Web Application

React Hooks: useState and useEffect in React.js Web Application

In this React Hooks tutorial, you'll see the real-world examples of React Hooks useState and useEffect in a React.js Web application. This React.js tutorial: React Hooks useState and useEffect Examples

The real-world examples of React Hooks useState and useEffect in a React.js Web application

In this React.js tutorial, we will show you the real-world examples of React Hooks useState and useEffect in a React.js Web application. Now, with these examples you will learn more deeply about a few essential React Hooks useState and useEffect.

Table of Contents:

  • Preparation
  • React Hooks useState Examples
  • React Hooks useEffect Examples
  • Run and Test the React Hooks useState and useEffect Examples

The following tools, frameworks, libraries, and modules are required for this tutorial:

  1. Node.js (with NPM or Yarn)
  2. React.js (min. version 16.8)
  3. Node-Express REST API
  4. Terminal or Node Command Line
  5. IDE or Text Editor (We are using Visual Studio Code)
Preparation

We need to prepare the environment for creating React.js application from the scratch, we assume you don't have any tools or frameworks installed on your computer. The first thing to do is install Node.js with NPM or Yarn. Just download the installer from official download page https://nodejs.org, choose recommended version for your OS. Install to your computer by following the install wizard. After Node.js installation finished, open your terminal (Mac OS/Linux) or Node command line (Windows) then check the installed Node.js and NPM.

node -v
v10.15.1
npm -v
6.13.1

If you plan to use Yarn, install it from the Terminal. On our Mac OS, we type this command.

brew install yarn

You can choose your desired installation method for your OS, you find the guide on the Yarn site. To check the installed yarn version, type this command.

yarn -v
1.19.2

Next, we will use the create-react-app tool. The create-react-app is a tool to create a React.js app from the command line or CLI. So you don’t need to install or configure tools like Webpack or Babel because they are preconfigured and hidden so that you can focus on the code. Type this command to install it.

sudo npm install -g create-react-app

Now, we can create a new React.js app using that tool.

create-react-app react-hooks-use

This command will create a new React app with the name react-hooks-use and this process can take minutes because all dependencies and modules also installing automatically. Next, go to the newly created app folder.

cd ./react-hooks-use

Open the project in your IDE or text editor and see the content of package.json.

"dependencies": {
  "react": "^16.12.0",
  "react-dom": "^16.12.0",
  "react-scripts": "3.2.0"
},

That React version is the version that already uses React Hooks as default. Now, src/App.js doesn't use class anymore. For sanitation, run this React app for the first time by type this command.

yarn start

And here' we go the latest React.js application that uses React Hooks with the same initial home page.

Next, we need to add the required component or page for each example. For that, add these directories and files inside the src directory.

mkdir src/hooks-state
mkdir src/hooks-effect
touch src/hooks-state/BasicState.js
touch src/hooks-state/MultipleState.js
touch src/hooks-effect/BasicEffect.js
touch src/hooks-effect/FetchEffect.js
touch src/hooks-effect/AxiosEffect.js
touch src/hooks-effect/ListenerEffect.js

Next, add the react-router-dom to make routing and navigation for these examples.

yarn add react-router-dom

Next, open and edit src/index.js then add these imports of Router and Route (react-router-dom) and all created components before the ./index.css import.

import { BrowserRouter as Router, Route } from 'react-router-dom';
import BasicState from './hooks-state/BasicState';
import MultipleState from './hooks-state/MultipleState';
import BasicEffect from './hooks-effect/BasicEffect';
import FetchEffect from './hooks-effect/FetchEffect';
import AxiosEffect from './hooks-effect/AxiosEffect';
import ListenerEffect from './hooks-effect/ListenerEffect';

Next, add to the React.DOM render.

ReactDOM.render(
    <Router>
        <div>
            <Route render ={()=> < App />} path="/" />
            <Route render ={()=> < BasicState />} path="/hooks-state/basic-state" />
            <Route render ={()=> < MultipleState />} path="/hooks-state/multiple-state" />
            <Route render ={()=> < BasicEffect />} path="/hooks-effect/basic-effect" />
            <Route render ={()=> < FetchEffect />} path="/hooks-effect/fetch-effect" />
            <Route render ={()=> < AxiosEffect />} path="/hooks-effect/axios-effect" />
            <Route render ={()=> < ListenerEffect />} path="/hooks-effect/listener-effect" />
        </div>
    </Router>, document.getElementById('root'));

Next, we will create navigation for all React Hooks useState and useState examples. Open and edit src/App.js then replace all "return" content with these.

return (
  <div className="App">
    <h2>React Hooks Examples</h2>
    <h3>useState Examples:</h3>
    <p><a href="/hooks-state/basic-state">Basic useState Example</a></p>
    <p><a href="/hooks-state/multiple-state">Multiple useState Example</a></p>
    <h3>useEffect Examples:</h3>
    <p><a href="/hooks-effect/basic-effect">Basic useEffect Example</a></p>
    <p><a href="/hooks-effect/fetch-effect">Fetch useEffect Example</a></p>
    <p><a href="/hooks-effect/axios-effect">Axios useEffect Example</a></p>
    <p><a href="/hooks-effect/listener-effect">Listener useEffect Example</a></p>
    <p><a href="/hooks-effect/resize-effect">Resize useEffect Example</a></p>
  </div>
);

Next, make all components file have an exported function name same as the file name as an example below.

import '../App.css';

function BasicState() {

  return (
    <div className="App">
    </div>
  );
}

export default BasicState;
React Hooks useState Examples

Before seeing the example, we try to make a little understanding of the React Hooks useState formula. It just simply like this line.

const [state, setState] = useState(initialState);

Which "state" is a variable, "setState" if a function that can change the variable, and "initialState" is an initial value of the variable.

Basic useState Example

Next, we need to implement the React Hooks basic useState. Open and edit src/hooks-state/BasicState.js then add useState to the react import.

import React, { useState } from 'react';

We will declare a variable of the string that has an initial value.

const [title, setTitle] = useState('Getting Started learning a React Hooks');

In this basic useState usage, we will put the "setTitle" that change the "title" value as a button action. Add/replace these lines to the React template to add a text and a button.

return (
  <div className="App">
    <h2>{title}</h2>
    <button onClick={() => setTitle('React Hooks in My Learning Path')}>
      Change Title
    </button>
  </div>
);

Multiple useState Example

This example is the multiple "useState" with different data types. Open and edit src/hooks-state/MultipleState.js then add useState to the react import.

import React, { useState } from 'react';

Add these multiple constant variables.

const [title, setTitle] = useState('Getting Started learning a React Hooks');
const [year, setYear] = useState(2019);
const [emails, setEmail] = useState([
  {name: 'my name', email: '[email protected]'},
  {name: 'my second name',email: '[email protected]'},
  {name: 'my third name',email: '[email protected]'},
  {name: 'my alias name',email: '[email protected]'}
]);

Replace the "return" content with these.

return (
  <div className="App">
    <h2>{title}</h2>
    <h2>{year}</h2>
    <dl>
      {emails.map((item, idx) => (
        <dd>{item.name}, {item.email}</dd>
      ))}
    </dl>
    <button onClick={() => {
      setTitle('Another React Hooks Book');
      setYear(2020);
      setEmail([
        {name: 'Ian Rush', email: '[email protected]'},
        {name: 'Roberto Baggio',email: '[email protected]'},
        {name: 'Thierry Hendry',email: '[email protected]'},
        {name: 'David Seaman',email: '[email protected]'}
      ]);
    }}>
      Change Title
    </button>
  </div>
);
React Hooks useEffect Examples

React Hooks useEffect is a single function that similar to componentDidMount, componentDidUpdate, and componentWillUnmount in React classes. We will show you an example of the useEffect in a real React application.

Basic useEffect Example

This basic useEffect example just changes the title when the component is loaded. Open and edit src/hooks-effect/BasicEffect.js then add this import of useState and useEffect.

import React, { useState, useEffect } from 'react';

Add a required constant variable that uses useState.

const [title, setTitle] = useState('React Hooks at a Glance');
const [year, setYear] = useState(2019);

Add this useEffect function before the return template.

useEffect(() => {
  setTitle(`The new articles of ${year}`);
});

Change the return content with these.

return (
  <div className="App">
    <h2>{title}</h2>
  </div>
);

Fetch useEffect Example

This example fetches data from the REST API you can run our Node-Express REST API and MongoDB server in your machine to implementing this example. Open and edit src/hooks-effect/FetchEffect.js then add this import of useState and useEffect.

import React, { useState, useEffect } from 'react';

Add a required constant variable that use useState.

const [product, setProduct] = useState(null);
const [url, setState] = useState(`http://localhost:3000/api/v1/products`);

Next, we will implement useEffect with a cleaning-up effect feature by adding an AbortController method at the useEffect return. Add this useEffect function after the constant variable.

useEffect(() => {

  let abortController = new AbortController();

  const loadProduct = async () => {
    try {
      const response = await fetch(url, { signal: abortController.signal });
      const data = await response.json();
      console.log(`Data fetched: ${data}`);
      setProduct(data);
    } catch (error) {
      console.log(`Error fetch data: ${error}`);
      throw error;
    }
  };

  loadProduct();

  return () => {
    console.log("Cancel fetch: abort");
    abortController.abort();
  };
}, [url]);

If you want to implement conditionally firing an effect, just change the end of the useEffect function to this.

useEffect(() => {
  ...
}, [product]);

Next, add a loading indicator before implementing the view template.

if (!product) {
  return <div className="App">Loading...</div>;
}

Modify the return to display the loaded data.

return (
  <div className="App">
    {product.map((item, idx) => (
      <p>{item.prod_name}</p>
    ))}
  </div>
);

Axios useEffect Example

This example same as the previous fetch example except we are using the Axios library to consume the REST API. First, we need to install that library by type this command in the terminal.

yarn add axios

Next, open and edit src/hooks-effect/AxiosEffect.js then add these imports of useState, useEffect, and Axios.

import React, { useState, useEffect } from 'react';
import Axios from "axios";

Add a required constant variable that uses useState.

const [product, setProduct] = useState(null);
const [url, setState] = useState(`http://localhost:3000/api/v1/products`);

Next, we will implement useEffect with a cleaning-up effect feature by adding an Axios.CancelToken.source() cancel method at the useEffect return. Add this useEffect function after the constant variable.

useEffect(() => {
  let source = Axios.CancelToken.source();

  const loadProduct = async () => {
    try {
      const response = await Axios.get(url, {
        cancelToken: source.token
      });
      console.log(response);
      setProduct(response.data);
    } catch (error) {
      if (Axios.isCancel(error)) {
        console.log("Cancel axios data source on error");
      } else {
        throw error;
      }
    }
  };

  loadProduct();

  return () => {
    console.log("Cancel axios data source");
    source.cancel();
  };
}, [url]);

Next, add a loading indicator before implementing the view template.

if (!product) {
  return <div className="App">Loading...</div>;
}

Modify the return to display the loaded data.

return (
  <div className="App">
    {product.map((item, idx) => (
      <p>{item.prod_name}</p>
    ))}
  </div>
);

Listener useEffect Example

This example of React Hook's useEffect is firing an effect based on Javascript DOM event listener. Right now, we will use the click event lister for the windows or screen element. Open and edit src/app/hooks-effect/ListenerEffect.js then add this import of useEffect.

import React, { useEffect } from 'react';

Next, we will implement the useEffect that firing by click event listener.

useEffect(() => {
  const listener = () => {
    console.log(`The screen is clicked`);
  };
  window.addEventListener("click", listener);

  return () => {
    window.removeEventListener("click", listener);
  };
}, []);

Next, modify the return to this HTML template.

return (
  <div className="App">
    <h4>Click the screen to listen the click event</h4>
  </div>
);
Run and Test the React Hooks useState and useEffect Examples

To run this React Hooks useState and useEffect examples, we need to run MongoDB server and Node-Express REST API first in another terminal tabs. Next, run this React Hooks example using custom port by type this command.

PORT=8080 yarn start

Now, the React Hooks useState and useEffect Examples run using Port 8080 that different from the Node-Express server. You can check the implementation of these examples one by one from this React home page.

That it's, the React Hooks useState and useEffect Examples. You can find the full source codes from our GitHub.

Thanks!

Building Mobile Apps With Ionic And React for Beginners

Building Mobile Apps With Ionic And React for Beginners

React developers can get the advantages of Ionic to build hybrid mobile apps and progressive web apps. In this tutorial, we’ll be using Ionic and React to build a mobile app from scratch.

React developers can get the advantages of Ionic to build hybrid mobile apps and progressive web apps. In this tutorial, we’ll be using Ionic and React to build a mobile app from scratch.

Ionic has recently added support for React; so now

React developers can get the advantages of Ionic to build hybrid mobile apps and progressive web apps (PWAs). In this post, we’ll show you how to get started using Ionic with React by building a simple demo app from scratch.

Prerequisites

In order to properly follow this tutorial, you’ll need the following prerequisites:

  • recent versions of Node.js and npm installed on your system,

  • working knowledge of TypeScript and React.

You can check that you have the latest Node.js version (v10) installed by running the following command:

$ node -v
Introducing React And Ionic

Let’s start with brief introductions to both React and Ionic.

According to the official website:

“React is a declarative, efficient, and flexible JavaScript library for building user interfaces. It lets you compose complex UIs from small and isolated pieces of code called ‘components’.”

React focuses on building UIs and doesn’t provide any built-in tools for the common tasks required in web development, such as fetching remote data and routing, so you’ll need to use some existing third-party libraries for these tasks.

According to the Ionic website:

“Ionic Framework is the free, open-source mobile UI toolkit for developing high-quality cross-platform apps for native iOS, Android, and the web — all from a single codebase.”

Basically, it’s a set of UI components that you can use with plain JavaScript or any popular front-end framework or library, such as Angular, React or Vue, to build a hybrid mobile app and PWA.

In this tutorial, we’ll see and use some Ionic UI components such as the following:

  • IonMenu: With this, a navigation drawer will slide in from the side of the current view.

  • IonToolbar: These top bars are positioned above or below the content.

  • IonHeader: This parent component holds the toolbar component.

  • IonContent: This component provides a content area, with methods to control the scrollable area and other things. You need only one content component inside a single view.

  • IonList: This component contains items with similar data content, such as images and text. It’s made up of IonItem objects.

  • IonItem: This component may contain text, icons, avatars, images, inputs and any other native or custom element.

  • IonButton: This component provides a clickable element, which can be used in a form or anywhere that needs simple, standard button functionality.

Installing Ionic CLI V5

Ionic’s command line interface (CLI), version 5, has support for creating Ionic projects based on React. So, let’s get started by installing the tool from npm.

Open a CLI, and run the following command:

$ npm install -g ionic

At the time of writing, Ionic’s CLI v5.2.3 is the latest.

Note: According to how you installed Node.js in your system, you may need to add sudo before your command in macOS or Linux or run the command prompt as administrator in Windows if you get any permission errors. You can also simply fix your npm permissions or use a tool such as nvm.

Next, install Cordova Resources (which is used to generate Cordova resources locally) and Native Run (used to deploy app binaries to devices):

$ npm install -g cordova-res native-run

These are required only if you want to test your application on a real mobile device or emulator.

Creating An Ionic And React Project

Now, let’s create a project based on React. Go back to your terminal, make sure you are in your working directory, and run the following command:

$ ionic start myApp --type=react

We use --type=react to generate a project based on React. You’ll next need to choose a starter template from the available ones. Let’s pick sidemenu for a starter template with a side menu and navigation.

After generating the project and installing the dependencies, you can serve your app locally using the following commands:

$ cd ./myApp
$ ionic serve

Your app will be available from the http://localhost:8100 address, and you can actually use your web browser to start playing with it.

Ionic is called a hybrid mobile framework because it makes use of web technologies that were originally designed to create web apps, along with a native container (Cordova or Capacitor), to build mobile apps without using native technologies for the target platforms, such as Java or Kotlin for Android or Swift for iOS.

Because your mobile app is actually a web application, you can do most development by testing in a web browser without using an emulator or a real mobile device, except for testing native device features such as the camera or the SQLite storage, in case you’ve used them in your app. In fact, it’s even possible to use certain techniques to mimic the plugins that provide the native features and do the entire testing during development in your web browser.

Cleaning Our Project

We have the app’s basic structure, with two pages (home and list) and a menu. Let’s remove the list page since it’s just boilerplate code.

First, remove the src/pages/List.tsx file, then open the src/App.tsx file, and remove the entry for the list page from the appPages array:

const appPages: AppPage[] = [
  {
    title: 'Home',
    url: '/home',
    icon: home
  }
];

Also, remove the import of the list page from the file:

import  List  from  './pages/List';

Next, remove

<Route path="/:tab(home)/list" component={List} exact={true} />

from the App component:

const App: React.FunctionComponent = () => (
  <IonApp>
    <IonReactRouter>
      <IonSplitPane contentId="main">
        <Menu appPages={appPages} />
        <IonPage id="main">
          <IonRouterOutlet>
            <Route path="/:tab(home)" component={Home} exact={true} />
            <Route exact path="/" render={() => <Redirect to="/home" />} />
          </IonRouterOutlet>
        </IonPage>
      </IonSplitPane>
    </IonReactRouter>
  </IonApp>
);

export default App;

The App component is the root component that gets rendered by our application. If you open the src/index.tsx file, you’ll find the following code:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

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

The render() method is used to render a React element into the DOM in the supplied root element.

Theming The App

Having created, served and cleaned our Ionic project, let’s now see how we can change the colors of the UI so that it looks more professional.

Let’s get started with the side menu. Open the src/components/Menu.tsx file, and add a color attribute with a primary value to the IonToolbar, IonContent, IonList and IonItem UI components:

const Menu: React.FunctionComponent = ({ appPages }) => (
  <IonMenu contentId="main">
    <IonHeader>
      <IonToolbar color="primary">
        <IonTitle>Menu</IonTitle>
      </IonToolbar>
    </IonHeader>
    <IonContent color="primary">
      <IonList style= {{ background : '#3880ff'}} color="primary">
        {appPages.map((appPage, index) => {
          return (
            <IonMenuToggle key={index} auto-hide="false">
              <IonItem color="primary" href={appPage.url}>
                <IonIcon slot="start" icon={appPage.icon} />
                <IonLabel>{appPage.title}</IonLabel>
              </IonItem>
            </IonMenuToggle>
          );
        })}
      </IonList>
    </IonContent>
  </IonMenu>
);

Ionic provides some default colors (primary, secondary, tertiary, success, warning, danger, light, medium and dark) that can be used to change the color of UI components. A color can be applied to an Ionic component in order to change the default colors using the color attribute. See “Colors” for more information.

These colors have default values, but you can customize them via some predefined CSS variables. See “Modifying Colors“.

This is a screenshot of our menu:

Next, let’s change the color of the Home page. Open the src/pages/Home.tsx file, and set the color attribute of the IonToolbar and IonContent components to primary:

const HomePage: React.FunctionComponent = () => {
  return (
    <>
      <IonHeader>
        <IonToolbar color="primary">
          <IonButtons slot="start">
            <IonMenuButton />
          </IonButtons>
          <IonTitle>Home</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent color="primary" >

      </IonContent>
    </>
  );
};

This is a screenshot of the page:

Installing Axios And Consuming A REST API

We’ll see how to install Axios and consume a third-party RESTful API in our application, and we’ll also see how to display the fetched data using Ionic card and list components.

Having themed our application, let’s now see how to fetch data using Axios. We’ll use the third-party API available from NewsAPI.org.

First, we need to grab an API key, so that we can communicate with the API. Go to the registration page, enter your information, and register an account. You’ll be given an API key; note it, and let’s continue.

Head back to your terminal, and run the following command to install Axios:

$ npm install axios --save

Next, open the src/pages/Home.tsx file, and start by importing Axios and IonButton:

import {
  IonButton
} from '@ionic/react';

import axios from 'axios';

Next, define the API_KEY and URL constant variables:

const  API_KEY  =  "<YOUR_API_KEY_HERE>";
const  URL  =  `https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=${API_KEY}`;

In the URL variable, we’ll add an endpoint to get the top headlines from our source, TechCrunch. You can use any source you want from the available sources.

Note: Make sure to put your own API key in the API_KEY variable.

Next, define the fetchArticles() method as follows:

const fetchArticles = () => {

  return axios({
    url: URL,
    method: 'get'
  }).then(response => {

    console.log(response);
    return response.data;
  })
};

We simply call the axios() method to send a GET request to the news endpoint, and the result from the method is a promise that needs to be resolved in order to get the news data.

Next, update the HomePage component as follows to call the fetchArticles() method and resolve the returned promise:

const HomePage: React.FunctionComponent = () => {

  const [articles, setArticles] = React.useState([]);
  const items: any[] = [];

  React.useEffect(() => {

    fetchArticles().then(data => setArticles(data.articles));

  }, []);

  return (
    <>
      <IonHeader>
        <IonToolbar color="primary">
          <IonButtons slot="start">
            <IonMenuButton />
          </IonButtons>
          <IonTitle>Home</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent color="primary" >
        <IonList color="primary">

          {
            articles.map(a => {

              return (
                <IonItem>
                  {a['title']}
                  <IonButton href={a['url']} color="primary" slot="end">Read</IonButton>
                </IonItem>
              );
            })
          }

        </IonList>
      </IonContent>
    </>
  );
};

In our function component, we first call the useState() hook of React to create the articles state variable, which will hold the news articles after we fetch them from the API.

useState() returns the state variable, which has the empty array as its initial value and a function that we can use to change the value of the variable. We use destructuring assignment to unpack the values from the returned pair into distinct variables (i.e. articles and setArticles()).

Next, we call the useEffect() hook to perform a side effect in our component. In our case, the side effect is to fetch data from the news API using thefetchArticles()method, which returns a promise. Once the promise is resolved, we call the setArticles() method to assign the news data to the articles variable.

  • Both useState() and useEffect() are built-in React hooks that were introduced in React 16.8; they simply let you use state and other React features without having to write a class. The useEffect() hook is equivalent to calling the componentDidMount, componentDidUpdate and componentWillUnmount lifecycle methods combined in class-based components.

Finally, in the view template, we iterate over the articles array using the map() method, and we display the title of each news article inside an IonItem element of the IonList component, and also a button that takes us to the page of the full article.

This is a screenshot of the page:

You can find the source code in this GitHub repository.

Conclusion

In this tutorial, we have started using both Ionic and React and used them to build a simple mobile application that fetches and displays news data from a third-party API using the Axios client. We have also seen how to use hooks in React — namely, the useState() anduseEffect()hooks — to create state and perform side effects inside React function components. With Ionic, we’ve seen how easy it is to generate a project based on React and how we can theme the application using the color attributes of components.

How to Build an Ionic Chat App with React and Stream

How to Build an Ionic Chat App with React and Stream

In this tutorial, I'll walk you through how to build a lightweight Ionic chat application that is powered by React and Stream Chat.

In this tutorial, I’ll walk you through how to build a lightweight Ionic chat application that is powered by React and Stream Chat.

There are a few requirements for this, primarily the version of Node.js (I prefer to use nvm for Node version management), XCode for iOS if you’re on macOS or Android Studio if you’re on macOS or Windows and want to build against Android, and yarn for dependency management.

Let’s code!

1. Install Ionic

To get started with Ionic, download the Ionic CLI using npm:

$ yarn global add ionic

Once installed, login to Ionic from the command line using your new CLI:

$ ionic login

For now, that’s all that we have to do. We’re going to be using Create React App (next step) to continue our installation.

2. Install Create React App and Dependencies

Similar to how we installed Ionic, let’s go ahead and install Create React App (CRA) globally using npm:

$ yarn global add create-react-app

Next, create a new directory. I’m going to be working in my ~/Code directory, but you’re free to use a directory of your choosing:

$ cd ~/Code

Now, install React using CRA (ionic-chat is the name of the directory that will be generated — this is also optional as you can name it whatever you’d like):

$ npx create-react-app ionic-chat

Move into the ionic-chat directory and we’ll start installing the necessary dependencies.

$ yarn add stream-chat stream-chat-react axios react-router react-router-dom @ionic/react

With our dependencies installed, let’s go ahead and move on to the next step of the setup.

3. Setup the API with Heroku

The API, although small, plays an important role in chat. The API accepts user credentials from the login screen and generates a JWT for use within that. It also adds the user to the channel.

To spin up the API, I’ve included a simple one-click Heroku button. This will generate a new application on Heroku and then create a Stream Chat trial for you to use. After clicking the Heroku button, you will be prompted to add an application name — make this unique. Then click “Deploy” to kick off the Heroku deploy process.

Heroku Dashboard

Once installed, get the environment variables from Heroku (they were generated by the Heroku creation) and drop them in your .env file in your React app. The environment variables can be found under the “Settings” section of your Heroku dashboard as shown in this blog post by Heroku. Note that there is only one environment variable called “STREAM_URL”. The key and secret are delimited by a “:” with the first being the key and the second being the secret.

Heroku — Environment Variables

Alternatively, if you would like to skip Heroku, you can clone this GitHub repo and run the API with the yarn start command — be sure to run yarn install prior to starting and also be sure to fill out your .env with credentials found on the Stream dashboard (you will need to enable a free chat trial).

4. Install the iOS Simulator

If you have XCode installed, you’re pretty much all set. If not, and you want to download XCode, you can do so here. XCode comes bundled with an iOS Simulator by default.

Should you not wish to install XCode, you can optionally install this npm package which will install a standalone iOS simulator for you.

$ yarn global add ios-sim

The full instructions on how to use it are located here: https://www.npmjs.com/package/ios-sim

5. Install Android Studio (Optional)

Running on iOS with macOS seems to be the fastest way to test your code; however, if you’re on Windows or would simply like to use Android, I’ll cover that below.

Head over to the Android Studio download page and select your download of choice. Android Studio is available for iOS, Windows, and macOS. It’s a large file so the download may take a good amount of time.

Once downloaded, follow the installation instructions and open Android Studio. We’re going to download the necessary SDKs and create an Android Virtual Device (AVD).

With Android Studio open, click on “Configure” and then click “SDK Manager”.

Android Studio — Configure

With the SDK Manager open, select “Android 9.0 (Pie)” and then click “Apply”.

Android Studio — Android 9.0 (Pie)

Your download will begin. Once complete, go back to the main screen and click the “Configure” button, followed by “AVD Manager”. On the AVD Manager screen, you will want to click “+ Create Virtual Device”.

Select the “Pixel 3 XL” device, then click “Next”. Select “Pie (28)” for your API level followed by the “Next” button.

Android Studio — OS Download

Finally, click “Finish” and your AVD will be provisioned. Once done, you can safely exit out of the AVD screen and you will see your newly created AVD in the AVD manager.

AVD Manager

If you click on the green play button, your AVD will launch!

Android Pie Emulator

Congratulations! You’ve successfully generated an AVD within Android Studio! We’re not going to use it just yet, but the AVD will come in handy when testing later on in this tutorial.

6. Create Files

We have everything set up, now it’s time to add the necessary files to make our code work! We’ll need to create a handful of files, so pay close attention:

  1. In the root of your directory, create ionic.config.json with the following contents:
{
  "name": "Ionic Chat",
  "type": "custom",
  "integrations": {}
}

ionic.config.json

  1. In public/index.html, swap out the current HTML for the following:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,
    minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover user-scalable=no"
    />

    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta
      name="apple-mobile-web-app-status-bar-style"
      content="black-translucent"
    />
    <meta name="theme-color" content="#ffffff" />
    <meta name="apple-mobile-web-app-title" content="Ionic Chat" />

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <title>Ionic Chat</title>
  </head>
  <body ontouchstart="">
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

public/index.html

  1. Move into the src/ directory, we’re going to create and modify a few files:

In app.css, swap out all of the existing CSS for this:

@import url("https://fonts.googleapis.com/css?family=Open+Sans");

html,
body {
  background: #ffffff;
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
    env(safe-area-inset-bottom) env(safe-area-inset-left);
  font-family: "Open Sans", sans-serif;
}

.no-scroll .scroll-content {
  overflow: hidden;
}

::placeholder {
  color: #3f3844;
}

.login-root {
  text-align: center;
  margin-top: 25%;
}

.login-card > h4 {
  margin-bottom: 22px;
}

.login-card > input {
  padding: 4px 6px;
  margin-bottom: 20px;
  border: 1px solid #d3d3d3;
  background: hsla(0, 0%, 100%, 0.2);
  border-radius: 4px !important;
  font-size: 16px;
  color: #24282e;
  -webkit-box-shadow: none;
  box-shadow: none;
  outline: 0;
  padding: 0 16px 1px;
  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  height: 50px;
  width: 300px;
}

.login-card button {
  font-size: 16px;
  background-color: #3880ff;
  border-radius: 4px;
  line-height: 1.4em;
  padding: 14px 33px 14px;
  margin-right: 10px;
  border: 0 solid rgba(0, 0, 0, 0);
  color: #ffffff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.12);
  border-radius: 6px;
  text-transform: none;
  outline: none;
}

.str-chat__loading-indicator {
  text-align: center;
  margin-top: 15%;
}

.str-chat-channel {
  background-color: #ffffff !important;
}

.str-chat__header-livestream {
  box-shadow: none !important;
  background: transparent;
}

.str-chat__square-button {
  display: none !important;
}

.str-chat__input {
  box-shadow: none !important;
}

.rta__textarea {
  padding: 4px 6px;
  margin-bottom: 20px;
  border: 1px solid #d3d3d3 !important;
  background: hsla(0, 0%, 100%, 0.2);
  border-radius: 4px !important;
  font-size: 14px !important;
  color: #24282e !important;
  -webkit-box-shadow: none !important;
  -webkit-appearance: none !important;
  box-shadow: none !important;
  outline: none !important;
  padding: 0 16px 1px;
  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  height: 50px;
}

.str-chat__textarea {
  height: 45px !important;
}

.str-chat__input-footer--count {
  margin-top: 4px;
  margin-left: 4px;
}

.footer {
  margin-bottom: 50px;
}

App.css

In App.js, swap out the existing code for this JavaScript (this logic will take care of routing between files):

import React from "react";
import { BrowserRouter as Router, Switch } from "react-router-dom";

import Chat from "./Chat";
import Login from "./Login";

import UnauthedRoute from "./UnauthedRoute";
import AuthedRoute from "./AuthedRoute";

const App = () => (
  <Router>
    <Switch>
      <UnauthedRoute path="/auth/login" component={Login} />
      <AuthedRoute path="/" component={Chat} />
    </Switch>
  </Router>
);

export default App;

App.js

Create a file called AuthedRoute.js and drop the contents below into the file:

import React from "react";
import { Redirect, Route } from "react-router-dom";

const AuthedRoute = ({ component: Component, loading, ...rest }) => {
  const isAuthed = Boolean(localStorage.getItem("token"));
  return (
    <Route
      {...rest}
      render={props =>
        loading ? (
          <p>Loading...</p>
        ) : isAuthed ? (
          <Component history={props.history} {...rest} />
        ) : (
          <Redirect
            to={{
              pathname: "/auth/login",
              state: { next: props.location }
            }}
          />
        )
      }
    />
  );
};

export default AuthedRoute;

AuthedRoute.js

Create a file named Chat.js and use the following code (this is all of the logic that powers chat):

import React, { Component } from "react";
import { IonApp, IonContent } from "@ionic/react";
import {
  Chat,
  Channel,
  ChannelHeader,
  Thread,
  Window,
  MessageList,
  MessageInput
} from "stream-chat-react";
import { StreamChat } from "stream-chat";

import "./App.css";
import "@ionic/core/css/core.css";
import "@ionic/core/css/ionic.bundle.css";
import "stream-chat-react/dist/css/index.css";
import "stream-chat-react/dist/css/index.css";

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

    const { id, name, email, image } = JSON.parse(localStorage.getItem("user"));

    this.client = new StreamChat(localStorage.getItem("apiKey"));
    this.client.setUser(
      {
        id,
        name,
        email,
        image
      },
      localStorage.getItem("token")
    );

    this.channel = this.client.channel("messaging", "ionic-chat", {
      image: "https://i.imgur.com/gwaMDJZ.png",
      name: "Ionic Chat"
    });
  }

  render() {
    return (
      <IonApp style={{ paddingTop: "2px" }}>
        <IonContent>
          <Chat client={this.client} theme={"messaging light"}>
            <Channel channel={this.channel}>
              <Window>
                <ChannelHeader />
                <MessageList />
                <div className="footer">
                  <MessageInput />
                </div>
              </Window>
              <Thread />
            </Channel>
          </Chat>
        </IonContent>
      </IonApp>
    );
  }
}

export default App;

Chat.js

Next, create a file called Login.js and use the following code (this will add auth to your app):

import React, { Component } from "react";
import axios from "axios";

import "./App.css";

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

    this.state = {
      loading: false,
      name: "",
      email: ""
    };

    this.initStream = this.initStream.bind(this);
  }

  async initStream() {
    await this.setState({
      loading: true
    });

    const auth = await axios.post(process.env.REACT_APP_TOKEN_ENDPOINT, {
      name: this.state.name,
      email: this.state.email
    });

    localStorage.setItem("user", JSON.stringify(auth.data.user));
    localStorage.setItem("token", auth.data.token);
    localStorage.setItem("apiKey", auth.data.apiKey);

    await this.setState({
      loading: false
    });

    this.props.history.push("/");
  }

  handleChange = e => {
    this.setState({
      [e.target.name]: e.target.value
    });
  };

  render() {
    return (
      <div className="login-root">
        <div className="login-card">
          <h4>Ionic Chat</h4>
          <input
            type="text"
            placeholder="Name"
            name="name"
            onChange={e => this.handleChange(e)}
          />
          <br />
          <input
            type="email"
            placeholder="Email"
            name="email"
            onChange={e => this.handleChange(e)}
          />
          <br />
          <button onClick={this.initStream}>Submit</button>
        </div>
      </div>
    );
  }
}

export default Login;

Login.js

Now, create a file called UnauthedRoute.js to accommodate for users who enter without being authenticated:

import React from "react";
import { Redirect, Route } from "react-router-dom";

const UnauthedRoute = ({ component: Component, loading, ...rest }) => {
  const isAuthed = Boolean(localStorage.getItem("token"));
  return (
    <Route
      {...rest}
      render={props =>
        loading ? (
          <p>Loading...</p>
        ) : !isAuthed ? (
          <Component history={props.history} {...rest} />
        ) : (
          <Redirect
            to={{
              pathname: "/"
            }}
          />
        )
      }
    />
  );
};

export default UnauthedRoute;

UnauthedRoute.js

Create a file called withSession.js:

import React from "react";
import { withRouter } from "react-router";

export default (Component, unAuthed = false) => {
  const WithSession = ({ user = {}, streamToken, ...props }) =>
    user.id || unAuthed ? (
      <Component
        userId={user.id}
        user={user}
        session={window.streamSession}
        {...props}
      />
    ) : (
      <Component {...props} />
    );

  return withRouter(WithSession);
};

withSession.js

  1. Install the Ionic build scripts in your package.json file:
"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "ionic:build": "react-scripts build",
    "ionic:serve": "react-scripts start"
}

package.json

Capacitor is an open-source framework provided by Ionic that helps you build progressive native web apps, mobile and desktops apps. It’s optimized for Ionic apps; however, it can be used with just about any framework.

We’ll be using Capacitor to lift and prepare our builds for iOS and Android. First things first though, let’s get Capacitor installed!

$ ionic capacitor add ios

Then, start the React app with the following command from your root directory:

$ yarn start

Open on iOS:

$ ionic capacitor open ios

Capacitor — iOS

Or, open on Android:

$ ionic capacitor open android

Capacitor — Android

Because I’m running macOS, I’m going to be using the iOS simulator. After running ionic capacitor open ios, XCode will launch. You will want to wait about a minute for it to index the project and then you can press the run button.

XCode

Your iOS simulator should boot up with the application installed and you should see a login screen similar to this:

iOS — Login

Go ahead and login with your name and email address. Don’t worry, your information is only stored in local storage and is not persisted to a third party platform of any kind. Once the chat window is loaded, you’ll be able to chat away!

iOS — Chat

What’s Next?

I would encourage you to continue developing against the codebase that you’ve created. If you have run into any issues, you can always clone the repo from GitHub for a fresh start.

In terms of deploying the application to a standalone device such as iOS or Android, Ionic has a great set of tutorials on how to do so. Both tutorials for iOS and Android publication can be found in the Ionic docs.

Want to know more about Stream Chat? Have a look at our interactive API tour that will walk you through the various steps of creating chat from scratch with Stream. We also have amazing API docs and a beautiful UI Kit that will allow you to build any type of chat.

Happy coding! Thank you for reading !