Getting Started With React Native

React Native

In this article you will learn how to set up your computer to begin developing apps using React Native.

Before we begin, what is React Native? As stated on the official React Native website, https://facebook.github.io/react-native/, “React Native lets you build mobile apps using only JavaScript. It uses the same design as React, letting you compose a rich mobile UI from declarative components.” The site goes on to say, “With React Native, you don’t build a ‘mobile web app’, an ‘HTML5 app’, or a ‘hybrid app’. You build a real mobile app that’s indistinguishable from an app built using Objective-C or Java. React Native uses the same fundamental UI building blocks as regular iOS and Android apps. You just put those building blocks together using JavaScript and React.”

You can learn more by reading the documentation at https://facebook.github.io/react-native/.

One important thing to keep in mind, is that, although React Native allows you to develop iOS and Android apps, you will need to have a Mac computer to build an iOS application.

Setting Up Your Computer

Depending on your experience, skills and what you plan on doing, you may either want to develop with Expo or with the React Native CLI. What’s the difference?

Expo is the easiest and quickest way to get started and build simple apps. There is no need to use Xcode or Android studio, simply use the Expo CLI to develop your app and run it on your phone using the Expo client app.

React Native CLI, on the other hand, allows more flexibility and control. With it you can do things, such as, using third party packages that require you to run the command “react-native link”. It also allows you to go in and make changes to native iOS and Android code. But the installation process will vary depending on the operating system your computer is running. Window users be aware that you will not be able to build iOS projects because iOS development requires a Mac. On the other hand, Mac using will be able to develop for both iOS and Android.

I will be walking you though, both the Expo and React Native CLI installation. Let’s begin with Expo. If you prefer Reactive Native CLI, please skip ahead to the Reactive Native CLI installation section.

Expo Installation

To use Expo, you need to install Node. The fastest and easiest way to install Node is through the website. Head to https://nodejs.org and choose a version. LTS is more stable, more likely to work, and the recommended option. Current is the latest version of Node but may not work with React Native. Choose one, download it and follow the instructions to install. Node will also install npm. Using the Node website will work for both Mac and Window users.

Once installed you can check the version of Node by opening the Terminal or Command Prompt and type the following and then press the “Enter” key:

node -v

Node will also install npm, to check the version type:

npm -v

If you are unsure how to open Terminal or Command Prompt, here is how to do so:

To open the Terminal on a Mac, open the Applications folder, then the Utilities folder and click on Terminal. Or press “Command” and ‘space” keys to open Spotlight and search for “Terminal”, which will be under “Application” section.

To open the Command Prompt on Windows, search for cmd in the Start menu. Or press the “Windows” and “R” key to open the Run window and search for “cmd”.

Version used in this article:

Node version = v10.15.3 and npm version = 6.9.0

Next install the Expo CLI command line utility. Do so by typing the following into your Terminal or Command Prompt:

npm install -g expo-cli

The next step is not mentioned in the React Native documentation, but the Expo documentation states that you will need Git on your computer to create projects. The link to Expo’s installation documentation is located here, https://docs.expo.io/versions/latest/introduction/installation/.

 

You can download Git here, https://git-scm.com/downloads. Download the correct version for your computer.

Start the Git setup. The Mac and Windows installation process differ. The Windows setup offers much more options than the Mac setup. I am not a Windows user, so I left all the settings as default and installed. You may need to restart your computer afterwards.

 

(Git installation on Windows)

The final step is to install the Expo client app on your phone.

Download the Expo client app for iOS from the App Store, https://itunes.apple.com/app/apple-store/id982107779. Or search for “Expo Client” in the App Store.

Download the Expo client app for Android from the Google Play Store, https://play.google.com/store/apps/details?id=host.exp.exponent&hl=en_US. Or search for “Expo Client” in the Google Play Store.

Having an Expo account will open up a few more options, such as publishing projects to your Expo portfolio. You can create an Expo account through the app or through their website, https://expo.io/. This step is optional.

React Native CLI Installation

React Native CLI has a series of steps that will vary for Mac and Window users. I will start by guiding you through the steps for those using Mac, then for those using Windows.

Mac Installation

Start by installing Homebrew, head to https://brew.sh, copy the script and paste it into the Terminal.

Once installed, run the following command in the Terminal:

brew install node
brew install watchman

Installing Node will also install npm.

Next step is to install the React Native CLI by typing:

npm install -g react-native-cli

iOS Set Up

Let’s first start by downloading Xcode, which is necessary for iOS builds.

You will find Xcode at https://itunes.apple.com/us/app/xcode/id497799835?mt=12, click “View in Mac app store” to download and install. Or open the Mac App Store and search for “Xcode”.

Next, install Xcode Command Line Tools. Open Xcode and choose “Preferences…” from  the Xcode menu on the upper left corner of the screen. The General window will open, go to Locations and toward the bottom, install the Command Line Tools by selecting the latest version from the dropdown.

At this point you are ready to create your first React Native project but if you want to develop for Android too, please follow the following steps.

Android Set up

Let’s focus on downloading what’s needed for Android builds.

First thing to do is install the Java SE Development Kit (JDK). You can find it at http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html.

Next, install Android Studio, you can find it at https://developer.android.com/studio/index.html.

Once installed open Android Studio, the Android Studio Setup Wizard will open. Click “Next” and when asked to choose between “Standard” and “Custom” installation, pick “Custom”.

Click “Next” and choose the theme you would like, then click “Next”. At this point it will ask you to check the components you would like to download. According to the React Native documentation, check Android SDK, Android SDK Platform, Performance (Intel HAXM) and Android Virtual Device.

 

(I had Android Studios already install and that is why I got a warning at the bottom.)

Click “Next” and leave the Emulator settings on the recommended setting and click “Next”, then “Finish”.

There are a couple more steps left.

Open Android Studio and in the lower right side of the window, click Android Studio click on “Configure” and “SDK Manager”.

Select the “SDK Platforms” tab, make sure you have “Show Package Details” checked on the bottom right side of the window. Select Android 9.0 (Pie) and make sure Android SDK Platform 28, Intel x86 Atom_64 System Image and Google APIs Intel x86 Atom System Image are checked.

Then click on the “SDK Tools” tab, making sure “Show Package Details” is checked on the lower right side of the window. Look under “Android SDK Build-Tools 29-rc2”, make sure 28.0.3 is selected.

Now click “Apply”, accept the user agreements and install.

Last step is to add a few lines to your “.bash_profile”. Open up Terminal and type:

nano .bash_profile

Then add the following lines:

export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools

 

To save the changes simply press “Control” and “X”. You will be asked to save changes, hit “Y” and then “Enter” to exit.

You’re all set for iOS and Android development on your Mac.

Windows Installation

Begin by installing Chocolatey, which can be found at  https://chocolatey.org/install/. Copy the command under “Install with cmd.exe” and paste it into the Command Prompt.

 

 

To open the Command Prompt, search for “cmd” in your Start menu, right click on it and select “Run as administrator”. A pop up will open asking for permission, select “Yes”. Copy the command provided into the Command Prompt and install.

Ensure that it is installed by typing:

choco

This will return the version and a command for the help menu.

Then type:

choco install -y nodejs.install python2 jdk8

After that, it is time to install the React Native CLI by typing the following into the Command Prompt:

npm install -g react-native-cli

I noticed that when I tried to do this, I got an error saying “npm” was not a recognized command. Restart your computer, open cmd as an administrator and try again.

Next, install Android Studio. You can find it at https://developer.android.com/studio/index.html. Open Android Studio, the Android Studio Setup Wizard will open. Click “Next” and when asked to choose between “Standard” and “Custom” installation, pick “Custom”.

(This window, and a few of the following, are screenshots taken on a Mac.)

Click “Next” and choose the theme you would like, then click “Next”. At this point it will ask you to check the components you would like to download. According to the React Native documentation, check Android SDK, Android SDK Platform, Performance (Intel HAXM) and Android Virtual Device. Click “Next” and leave the Emulator settings on the recommended setting and click “Next”, then “Finish”.

(Ignore the warning. This image is a screenshot from my Mac and I had previously installed Android Studios.)

Open Android Studio and in the lower right side of the window, click on “Configure” and “SDK Manager”.

Select the “SDK Platforms” tab, make sure you have “Show Package Details” checked on the bottom right side of the window. Select Android 9.0 (Pie) and make sure Android SDK Platform 28, Intel x86 Atom_64 System Image and Google APIs Intel x86 Atom System Image are checked.

Then click on the “SDK Tools” tab, making sure “Show Package Details” is checked on the lower right side of the window. Look under “Android SDK Build-Tools 29-rc2”, make sure 28.0.3 is selected.

 

Next search for “Environment variables” in the Start menu, and select “Edit the system environment variables”. When the window opens, click on the “Advanced” tab and towards the bottom select the “Environment Variables”. Under “User variables for YOUR_USERNAME”, click “New” and add the following:

Variable name = “ANDROID_HOME”
Variable value = “c:\Users\YOUR_USERNAME\AppData\Local\Android\Sdk”

You can find the location of the SDK by looking in the “SDK Manager” settings of Android Studio.

 

Next select “Path” in the “User variables for YOUR_USERNAME” and select “Edit”. A new window will open, click “New” and add the following:

c:\Users\YOUR_USERNAME\AppData\Local\Android\Sdk\platform-tools

Close the “Edit environment variable”, “Environment Variables” and “System Properties” windows by clicking “Ok”.

You are all set! Let’s begin by creating an Expo project, if you decide to use only React Native, please skip ahead.

Creating Expo Project

From here on out I will be working on a Mac, the steps will be the same on Windows but I will not be using the Command Prompt. I will be using commands, such as “clear”, that will work in the Terminal but may not work in the Command Prompt.

Open up a new Terminal and head to the directory you wish to save the project in.

I like to save my projects onto my Desktop, so I will type:

cd Desktop

Once in the directory of your choice, type:

expo init FirstProject

“expo init” initializes a directory called “FirstExpoProject”. If you prefer to name the project something else, replace “FirstExpoProject” with the name of your choice.

There will be an option given to “Choose a template”, select “Blank” and press “Enter”.

Next, you are prompted to enter “The name of your app visible on your home screen”. I will type “FirstExpoProject” and press “Enter”.

Congrats! You created your first React Native app. To enter the folder type:

cd PROJECT_NAME

And the follow to start it:

expo start

Or

npm start

A new tab will open in your web browser, called the Expo Dev Tool. Depending on your computer this may take some time.

You will have a few options to view the app.

One way to view the app is to use the QR code. In the Terminal and Expo Dev Tool you will see a QR code. You can scan the code with the Expo app on Android or the iPhone’s camera.

Make sure your computer and phone are on the same network when the “Connection” in Expo Dev Tools is set to “LAN”. If you select “Tunnel”, then both devices can be on different networks.

Another way to view the app is to type “e” in the Terminal, allowing you to send an email or text message with a link to the app. You will have the same option in the browser.

And the final option I will discuss, is to open the app in the Android emulator or iOS simulator, which requires you to first install Android Studio and Xcode. If you want to do this, please look at the section titled “React Native CLI”.

Once you have it running it will look like this:

It’s now time to see what we created! You will need a text editor for this part and my personal preference is Visual Studio Code, which can be downloaded here https://code.visualstudio.com/. There are other options, such as Atom and Sublime, for example. Since I will be using Visual Studio Code, some of the following steps may differ if you are using a different editor.

Open Visual Studio Code, and open the “FirstExpoTutorial” project. On the left side of the editor you will see the “Explorer” section. If you do not see it, click on “View” at the top of the screen and then on “Explorer”.

In the “Explorer”, open up App.js file, this is where we will modify the code.

 

The first line of code imports the default export React from the module react.

The second line is importing components from React Native. React Native has a bunch of built in components and apis which you can see here, https://facebook.github.io/react-native/docs/components-and-apis.

Next comes the class App, which has the function render(), that returns JSX code, similar to HTML, and is exported.

The JSX consist of a View, a container that supports layout, and Text, a React component for displaying text. Replace the text between the <Text></Text> tags, with:

<Text>Hello World! Welcome to my new expo app!</Text>

Save the file and the app will reload with the new text.

The last block of code you will notice is the constant named styles. This is the styling that is passed to the View component and is similar to CSS styling.

Creating React Native Project

The steps to create a project using the React Native CLI are similar to Expo. Use the command “cd” to move directories. I will use the following command to move to the Desktop directory, but you can choose any directory:

cd Desktop

Once you are in the directory you wish to create the project in, type :

react-native init FirstRNProject

“react-native init” initializes a directory called “FirstRNProject”. You can choose to name your project something else.

During the initialization it may suggest that you use Yarn to speed it up. This is optional but if you want Yarn you can download it at https://yarnpkg.com/en/docs/install#mac-stable. There is a Homebrew option I will use on Mac, type the following in the terminal:

brew install yarn

In Windows either use the Installer or Chocolatey. To use Chocolatey, type the following into the Command Prompt as an administrator:

choco install yarn

Congrats! You created your first React Native app. Now it’s time to run the app. After creating the project, you may have noticed that there are instructions on how to run it. First thing to do is to enter the project folder by typing the following into the Terminal:

cd FirstRNProject

If you are on a Mac, you can run you iOS project two ways. One is to type the following into the Terminal:

react-native run-ios

This command will start building your app and will open it on the iOS simulator. Second way to run the project is to open the “YOUR_PROJECT_NAME.xcodeproj” file inside the “ios” folder of the React Native project. Xcode will open up, select the simulator and click on the run button at the top left side of the window.

This is how the app will look on the simulator:

If you are using Windows or Mac and want to run the app on Android, start by opening up Android Studios to start up the emulator before running the command in the Terminal.

Select “Open an existing Android Studio project” from the “Welcome to Android Studio” window.

Navigate to the directory where the project is located and the select the “android” folder inside and click “Open”. There may be a popup asking you to update the Android Gradle plugin. Don’t upgrade because it may cause the project not to work, I clicked “Don’t remind me again for this project”.

Before running the command in the Terminal, there is one last thing to do and that is to check the AVD Manager. Go to Tools in the top of the screen and click on “AVD Manager”. The Android Virtual Device Manager window will open. I have a device already set up using Android 9.0.

If you don’t see a device or want to create a new one, click on “Create Virtual Device”. Select the device you want and click “Next”. On the following screen select “Pie”, download if needed” and click “Next”.

Give the device a custom name or leave it as default and click “Finish”. Choose a device to run and click on the green play button under “Actions”.

Once the emulator is running, go back to the Terminal and inside the project you created run the following command:

react-native run-android

Here is how the project will look in the emulator:

It’s now time to see what we created! You will need a text editor for this part and my personal preference is Visual Studio Code, which can be downloaded here https://code.visualstudio.com/. There are other options, such as Atom and Sublime, for example. Since I will be using Visual Studio Code, some of the following steps may differ if you are using a different editor.

Open Visual Studio Code, and open the FirstTutorial project. On the left side of the editor you will see the Explorer section. If you do not see it, click on “View” at the top of the screen and then on “Explorer”.

Since the project was created with the React Native CLI, you will notice that this project has more folders than an Expo project. Two folders that stand out are the Android and iOS folders, which will give you access to native code.

In the Explorer, open up App.js file, this is where we will modify the code.

The first line of code, “import React from ‘react’”, imports the default export React from the module react.

The second line is importing components from React Native. React Native has a bunch of built in components and apis which you can see here, https://facebook.github.io/react-native/docs/components-and-apis.

The React Native project will then have a constant named “instructions”. What it does is determine which kind of phone that app is running on and will display text accordingly.

Next comes the class App, which has the function render(), that returns JSX code, similar to HTML, and is exported.

The JSX consist of a View, a container that supports layout, and a few Text, a React component for displaying text. Delete the bottom two, then replace the text between the <Text></Text> tags, with:

<Text>Hello World! Welcome to my new React Native app!</Text>

Save the file and if the text on the simulator/emulator does not change, simply reload the app. To do so on the iOS simulator press “Command” and “D” and click reload. In the Android emulator press “Command” and “M” if running it on Mac or “Control” and “M” on Windows.

The last block of code you will notice is the constant named styles. This is the styling that is passed to the View component and is similar to CSS styling.

Now What?

Congratulations! You have installed everything necessary to create a React Native project, started the project and modified the code. Take a look at the React Native documentation or Expo documentation and play around with the project. Add more text, change the background color, add an image or a button. This is just the beginning, in no time you will be creating amazing apps and publishing them for the world to see.

What are React Hooks and why you should care about them – Part 2

Outline

  1. Context Hook
  2. Custom Hooks
  3. Important Rules with Hooks
  4. Sample application

In the first part of the blog, we discussed the main concepts of React Hooks and the reasons for using them. We compared the traditional ways of creating components and the new ways with hooks. In the second part, we will continue exploring the the different types of hooks, learn about the important rules and create a sample application. We have a lot to cover. Let’s get started!

Context Hook

What is a context?

First of all, what is a context in React?

Context is a way to share global data across components without passing props. Usually, data in React application is passed from parent to child through props. Sometimes we have some data that should be delivered to a component deep inside the component tree. It is tedious to manually pass the same data over and over again through all components. Instead, we can create a central store that can be inserted into any component, just like in Redux.

How about we see an example code without Context API and identify the need for using it.

Let’s say there is a banana  plantation called “App” and it sets the prices for all bananas in the world. Before bananas reach the end customers, they need to go through wholesalers and supermarkets first. After that we can go to the stores and buy them. But since wholesalers and supermarkets need to add their profit margins in the process, the cost of the bananas go up.

Without Context API

// App.js
import React, { useState } from 'react';

const App = () => {
  const [bananaPrice, setBananaPrice] = useState(2); // Original price: $2
  return <Wholesaler price={bananaPrice}/>;
}

const Wholesaler = (props) => {
  const price = props.price + 2;
  return <Supermarket price={price}/>;
}

const Supermarket = (props) => {
  const price = props.price + 3;
  return <Customer price={price}/>;
}

const Customer = (props) => {
  return (
	<div>
		<p>Plantation price: $2</p>
		<p>Final price: ${props.price} :)</p>
	</div>
  );
}

What if we want to buy the bananas straight from the plantation and avoid the middlemen?

Now, the same code with Context API

With Context API

// App.js
import React, { useState } from 'react';

// First we need to create a Context
const BananaContext = React.createContext();

// Second we need to create a Provider Component
const BananaProvider = (props) => {
  const [bananaPrice, setBananaPrice] = useState(2); // Original price: $2
  return (
    <BananaContext.Provider value={bananaPrice}>
      {props.children}
    </BananaContext.Provider>
  );
}

const App = () => {
  return ( // Wrap the component in Provider, just like in Redux
    <BananaProvider>
      <Wholesaler/>
    </BananaProvider>
  );
}

const Wholesaler = () => {
  return <Supermarket/>;
}

const Supermarket = () => {
  return <Customer/>;
}

const Customer = () => {
  return (
    <BananaContext.Consumer>
      {(context) => (
        <div>
          <p>Plantation price: $2</p>
          <p>Final price: ${context} 😃</p>
        </div>
      )}
    </BananaContext.Consumer>
  );
}

useContext()

So how can we use Context API with hooks? With useContext!

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

// First we need to create a Context
const BananaContext = React.createContext();

// Then we need to a Provider Component
const BananaProvider = (props) => {
  const [bananaPrice, setBananaPrice] = useState(2); // Original price: $2
  return (
    <BananaContext.Provider value={bananaPrice}>
      {props.children}
    </BananaContext.Provider>
  );
}

const App = () => {
  return ( // Wrap the component in Provider, just like in Redux
    <BananaProvider>
      <Wholesaler/>
    </BananaProvider>
  );
}

const Wholesaler = () => {
  return <Supermarket/>;
}

const Supermarket = () => {
  return <Customer/>;
}

const Customer = () => {
  const context = useContext(BananaContext);
  return (
    <div>
      <p>Plantation price: $2</p>
      <p>Final price: ${context} :)</p>
    </div>
  );
}

I know you could remove the line where you add the price to the cost of the banana and still get $2 at the end. But that is not the point. The point is that you have to do the props drilling when you don’t use Context. Incrementing the price while passing the the components is sort of the cost to do the props drilling.

Custom Hooks

Why?

Why would we want to create our own hooks? Because of 1 reason – it makes component logic more reusable.

  • Custom hook = Reusable logic

How?

How do we create a custom hook? Since hooks are just JavaScript functions, we can create a custom hook by just making a function.

  • The only difference is that the function name must start with the word – use. For example, useFunctionNameuseHungerStatus etc.
  • Custom hooks can call other hooks

Example without custom hook

Say we want to create an application with multiple Stopwatch timers on a single page. How would we do that?

This is what I mean by multiple timers:

Here is the code that implements hooks but does not reuse the logic for the timers

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

const App = () => {
  const [timerOneStatus, setTimerOneStatus] = useState(false);
  const [timerOneElapsed, setTimerOneElapsed] = useState(0);

  const [timerTwoStatus, setTimerTwoStatus] = useState(false);
  const [timerTwoElapsed, setTimerTwoElapsed] = useState(0);

  useEffect(() => {
    let intervalOne;
    if (timerOneStatus) {
      intervalOne = setInterval(
        () => setTimerOneElapsed(prevTimerOneElapsed => prevTimerOneElapsed + 0.1),
        100
      );
    }
    return () => clearInterval(intervalOne);
  }, [timerOneStatus]);

  useEffect(() => {
    let intervalTwo;
    if (timerTwoStatus) {
      intervalTwo = setInterval(
        () => setTimerTwoElapsed(prevtimerTwoElapsed => prevtimerTwoElapsed + 0.1),
        100
      );
    }
    return () => clearInterval(intervalTwo);
   }, [timerTwoStatus]);

  const handleReset1 = () => {
    setTimerOneStatus(false);
    setTimerOneElapsed(0);
  };

  const handleReset2 = () => {
    setTimerTwoStatus(false);
    setTimerTwoElapsed(0);
  };

  return (
    <div>
      <div>
        <h2>Stopwatch 1</h2>
        <h1>{timerOneElapsed.toFixed(1)} s</h1>
        <button onClick={() => setTimerOneStatus(!timerOneStatus)}>
          {timerOneStatus ? "Stop" : "Start"}</button>
        <button onClick={handleReset1}>Reset</button>
      </div>
      <div>
        <h2>Stopwatch 2</h2>
        <h1>{timerTwoElapsed.toFixed(1)} s</h1>
        <button onClick={() => setTimerTwoStatus(!timerTwoStatus)}>
          {timerTwoStatus ? "Stop" : "Start"}</button>
        <button onClick={handleReset2}>Reset</button>
      </div>
    </div>
  );
}

As we can see, we are not DRY coding here. Logic for the timers need to repeated every time we want to create a new timer. Imagine if we had 10 timers on one page.

Example with custom hook

Now what we could do is to separate the timer logic as a custom hook and use that one hook for creating any number of timers. Each timer would have its own state and action items. In the main component we use the custom hook just like useState or useEffect and destructure returned parameters from the hook.

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

const App = () => {
  const [timerOneStatus, setTimerOneStatus, timerOneElapsed, resetTimerOne] = useTimer();
  const [timerTwoStatus, setTimerTwoStatus, timerTwoElapsed, resetTimerTwo] = useTimer();

  return (
    <div>
      <div>
        <h2>Stopwatch 1</h2>
        <h1>{timerOneElapsed.toFixed(1)} s</h1>
        <button onClick={() => setTimerOneStatus(!timerOneStatus)}>
          {timerOneStatus ? "Stop" : "Start"}</button>
        <button onClick={() => resetTimerOne()}>Reset</button>
      </div>
      <div>
        <h2>Stopwatch 2</h2>
        <h1>{timerTwoElapsed.toFixed(1)} s</h1>
        <button onClick={() => setTimerTwoStatus(!timerTwoStatus)}>
          {timerTwoStatus ? "Stop" : "Start"}</button>
        <button onClick={() => resetTimerTwo()}>Reset</button>
      </div>
    </div>
  );
}

const useTimer = () => {
  const [status, setStatus] = useState(false);
  const [elapsedTime, setElapsedTime] = useState(0);

  useEffect(() => {
      let interval;
      if (status) {
        interval = setInterval(() =>
          setElapsedTime(prevElapsedTime => prevElapsedTime + 0.1),
          100
        );
      }
      return () => clearInterval(interval);
    },[status]);

  const handleReset = () => {
    setStatus(false);
    setElapsedTime(0);
  };

  return [status, setStatus, elapsedTime, handleReset];
}

In this case, we can place the hook in another file with other custom hooks and call it from anywhere in our project. Much cleaner and more reusable!

Rules with Hooks

Even though hooks are just functions, react team recommends to follow certain rules when using them. If you are lazy or just want to make sure you are following the rules automagically, you can install this linter. However, it is important that we have some common knowledge about the rules.

Rule 1 – Call hooks only at the top level

What does that mean?

It means we should not call hooks inside conditions, loops or nested functions. Rather use them at the top level of React functions.

Rule 2 – Hooks cannot be called from regular JavaScript functions. Call them from React functions

You can call hooks from the following functions:

  • Custom hooks
  • React function components

Rule 3 – Always start your custom hooks’ name with the word ‘use’

Sample application

Now let us build an application that takes advantage of the most hooks we have covered so far.

We will be building an application called Caturday and it will fetch pictures of random cats on the internet and allow us to vote on the pictures. It will keep count of our likes and dislikes. We will also add a button that can turn our page into dark mode (just change the background color of the page for simplicity).

Here is what the final result will look like: Link to Demo | Github

Step 1

We start building our app by running

$ create-react-app caturday

(If you don’t have create-react-app module installed, please run npx create-react-app caturday)

After navigating into the project folder, run

$ npm install semantic-ui-react --save

to install the Semantic UI design tool that will make dealing with CSS much easier.

Step 2

Create 3 files in the /src folder:

  • Caturday.js,
  • Caturday.css
  • customHooks.js

Step 3

Open the Caturday.css file and copy paste the following:

@media only screen and (max-width: 600px) {
  #image-container {
    width: 100%;
    padding-top: 50px;
  }
}
@media only screen and (min-width: 600px) {
  #image-container {
    width: 50%;
    margin: auto;
    padding-top: 50px;
  }
}
.dark-mode {
  background-color: #3b3939;
}
.main-img {
  margin: auto;
  height: 30em !important;
}
.main-placeholder {
  height: 30em !important;
}

Step 5

We create 2 custom hooks to use in our application. Open customHooks.js file and add these hooks

import { useState, useEffect } from "react";

export const useFetchImage = (url, likeCount, mehCount) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    fetch(url)
      .then(j => j.json())
      .then(data => {
        console.log(JSON.stringify(data))
        setData(data);
        setLoading(false);
      })
      .catch(err => {
        console.log(err);
      });
  }, [likeCount, mehCount]);
  return { data, loading };
};

export const useDarkMode = () => {
  const [enabled, setEnabled] = useState(false);
  useEffect(
    () => {
      enabled
        ? document.body.classList.add("dark-mode")
        : document.body.classList.remove("dark-mode");
    },
    [enabled] // Only re-runs when 'enabled' changes
  );
  return [enabled, setEnabled]; // Return state and setter
};

Step 5

We start constructing the primary file Caturday.js by importing the css file and creating a container for the app. We will also define the state of component and a customs hooks that update the image url when Like/Dislike buttons are clicked.

import React, { useState, useEffect } from "react";
import { Header, Segment, Image, Placeholder, Button, Container, Label, Icon, Checkbox } from "semantic-ui-react";
import './Caturday.css';

const Caturday = () => {
  const [likeCount, setLikeCount] = useState(0);
  const [mehCount, setMehCount] = useState(0);
  const [darkMode, setDarkMode] = useDarkMode();
  const {data, loading} = useFetchImage(
    "https://api.thecatapi.com/v1/images/search",
    likeCount,
    mehCount
  );
  
  return (
	<Container id="main-container">
      <Segment raised>
        // Header
        // Dark Mode toggle
        // Image container
        // Like and Dislike buttons
      </Segment>
    </Container>
  );
};

export default Caturday;

Step 6

Now when we define each element of our Caturday component and put all pieces together, our Caturday.js file should look like this.

import React, { useState, useEffect } from "react";
import { Header, Segment, Image, Placeholder, Button, Container, Label, Icon, Checkbox } from "semantic-ui-react";
import './Caturday.css';
import {useDarkMode, useFetchImage} from './customHooks';

const Caturday = () => {
  const [likeCount, setLikeCount] = useState(0);
  const [mehCount, setMehCount] = useState(0);

  const [darkMode, setDarkMode] = useDarkMode();
  const { data, loading } = useFetchImage(
    "https://api.thecatapi.com/v1/images/search",
    likeCount,
    mehCount
  );

  return (
    <Container id="image-container">
      <Segment raised>
      <Header className="ui basic segment centered">Caturday</Header>
        <Segment>
          <Checkbox onChange={() => setDarkMode(!darkMode)} toggle floated='right' label="Dark mode"/>
        </Segment>
        {
          loading ?
          <Placeholder fluid><Placeholder.Image className="main-placeholder"/></Placeholder> :
          <Image src={data[0] ? data[0].url : ''} className="main-img"/>
        }
        <Segment textAlign="center">
          <Button as="div" labelPosition="right">
            <Button onClick={() => setLikeCount(likeCount+1)} icon color="green">
              <Icon name="heart" />
              Like
            </Button>
            <Label as="a" basic pointing="left">
              {likeCount}
            </Label>
          </Button>
          <Button as="div" labelPosition="left">
            <Label as="a" basic pointing="right">
              {mehCount}
            </Label>
            <Button onClick={() => setMehCount(mehCount+1)} color="red" icon>
              <Icon name="thumbs down" />
              Meh
            </Button>
          </Button>
        </Segment>
      </Segment>
    </Container>
  );
};

export default Caturday;

Step 7

Open the App.js file and replace the return content with Caturday component

import React from 'react';
import Caturday from './Caturday';

const App = () => {
	return (
      <Caturday/>
    );
}

export default App;

Conclusion

We have covered most of the concepts about hooks and that should be enough for you to get started. If you have a project that you are working on right now that implements states and components in a traditional way, it is absolutely fine. No need to convert everything into hooks. However, when you are about to create new components, just give the hooks a try. They are 100% compatible with regular components with classes. You will be surprised how many lines of code you will be avoiding to accomplish the same functionalities. If you need more information about hooks, please checkout the official documentation by Facebook.

Thanks for spending your time to read the article!

What are React Hooks and why you should care about them – Part 1

Outline

  1. Intro
  2. What is wrong with React Components now?
  3. Hooks overview
  4. useState
  5. useEffect
  6. TLDR

Intro

There is a new kid on the block . React introduced a new feature called Hooks which will improve the code quality even more and make creating user interfaces easier. From now on, if you are going to create a new project, you should definitely take advantage of the new addition and keep your projects lean and clean. It was actually released a while ago, but production ready stable version has come out recently. So now is the time to really start using them. In this article, we will cover the main concepts and look at some examples. At the end of the article, you will have a fair amount of idea about React Hooks and you can start implementing them in your applications.

Before we dive in to the details of hooks, let us take a step back to understand the motivation behind it.


What is wrong with React components now?

3 things:

  1. Classes
  2. Hierarchical abyss (Not reusing stateful logic)
  3. Big components

1. Classes

Currently, there are mainly two ways to create components in React. First way is by using stateless functions:

function Greet(props){
	return <h1>Hello there, {props.name}!</h1>;
}

Second, using ES6 Classes:

class Greet extends React.Component {
	render(){
		return <h1>Hello there, {props.name}!</h1>;
	}
}

Right, so why are you saying there is something wrong with those two methods, you ask?

Well, first of all, there are no classes in JavaScript. A class is a syntactical sugar over JavaScript’s prototype-based inheritance. In other words, it is just a function with special features, which creates extra work for browser to process. But that is not the problem here. The problem is, classes are harder to understand and do not play well with minifying. They cause issues with hot reloading. Also, people often struggle when deciding to use classes or functions to make the components. Which results in inconsistency.

So, why not to use just functions then? Functions are stateless. Meaning, we cannot manage state in them. We can pass props back and forth, but that makes it hard to keep track of the changes.

2. Hierarchical Abyss

Just take a look at the picture below.

Extreme level of nested component tree makes it difficult to follow the data flow through the app.

3. Big components

Whether we like it or not, at some point of the development, our application gets large and requires more complex components. When that happens, our components end up implementing multiple React lifecycles that might contain unrelated data. A single task can be split across different lifecycles. That creates an opportunity for bugs in the application.


Hooks to the rescue!

Hooks solve all of the issues mentioned above. It does that by allowing us to add state management to functional components and use other React features.

Say what?

See, it is usually preferred to use just functions to create components. But as we mentioned above, we cannot manage the state or use lifecycles in our functions. But with the hooks we can!

(If you are thinking, why not use Redux for state management, hold on to that thought. That’s a discussion for another time.)

State Hook

Let’s look at an example code that changes a text when we click a button.

import React, { useState } from 'react';

function FruitMaster(){
	const [fruit, setFruit] = useState('Banana');
	return (
		<div>
			<p>Selected:{fruit}</p>
			<button onClick={
				() => setFruit(fruit=='Banana'?'Apple':'Banana')
			}>Change</button>
		</div>
	);
}

This is what supposed to happen:

We have a selected text variable, which is set to Banana by default. When we click on the Change button it changes the text from Banana to Apple or vice versa.

Now, let us break down the new elements in the component.

What are those things in the state variable?

In this case, setFruit() is equivalent of this.setState(). There is one important difference though. When we use this.setState(), it merges the changes to the state object. State hook on the other hand, will completely replace the state with the given value.

We used a state hook called useState in this example. There are other hooks too. We will see them soon.

So a hook, is actually a function that uses React features and returns a pair of values: one to hold the state value and one function to manage the value. We can name those values whatever we want. We can set a default value by passing it to the useState function.

Note that we are using a destructuring assignment to retrieve the pair of values. If you are not familiar with this method, take a look at here. Having said that, we could actually get the two values this way too:

const stateVariable = useState('Cherry'); //Returns an array with 2 values
const fruit = stateVariable[0];
const setFruit = stateVariable[1]; //function

Converting

Now, let’s convert our functional component to a class based component:

import React from 'react';

export default class FruitMaster extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      fruit: 'Banana'
    };
    this.setFruit = this.setFruit.bind(this);
  }

  setFruit(value){
    this.setState({fruit: value});
  }

  render(){
    return (
      <div>
        <p>Selected: {this.state.fruit}</p>
        <button onClick={
          () => this.setFruit(this.state.fruit == 'Banana' ? 'Apple' : 'Banana')
          }>Change</button>
      </div>
    );
  }
}

Comparison

We can see from the picture that using hooks reduces code volume almost by half!

Now, let us address the elephant in the room. What do we do when we have more than one variable in state?*

Simple! Create more state variables.

const [fruit, setFruit] = useState('Banana');
const [food, setFood] = useState('Taco');

Multiple state variables

import React, { useState } from 'react';

function FruitMaster(){
	const [fruit, setFruit] = useState('Banana');
	const [food, setFood] = useState('Taco');
	return (
		<div>
			<p>Fruit: {fruit}</p>
			<p>Lunch: {food}</p>
			<button onClick={
				() => setFruit(fruit=='Banana'?'Apple':'Banana')
			}>Change Fruit</button>
			<button onClick={
				() => setFood(food=='Taco'?'Burger':'Taco')
			}>Change Lunch</button>
		</div>
	);
}

What if I want to store all variables in one object? you might ask. Well, go ahead. But one thing to remember is that, state hook function replaces the stateand not merges to it. this.setState() merges the given values to the state object, hook function does not. But there is a way to fix it. Let us see how:

import React, { useState } from 'react';

function MealMaster(){
	  const [myState, replaceState] = useState({
	    fruit: 'Apple',
	    food: 'Taco'
	  });
	  return (
	    <div>
	      <p>Fruit: {myState.fruit}</p>
	      <p>Lunch: {myState.food}</p>
	      <button onClick={
	        () => replaceState(myState => ({
	          ...myState,
	          fruit: myState.fruit=='Banana'?'Apple':'Banana'
	        }))
	      }>Change Fruit</button>
	      <button onClick={
	        () => replaceState(myState => ({
	          ...myState,
	          food: myState.food=='Taco'?'Burger':'Taco'
	        }))
	      }>Change Lunch</button>
	    </div>
	 );
}

We have to use spread operator to change the only part of the state we need and keep the rest of it as it is.

Effect Hook

What about the lifeCycle methods of React? We could use those with classes. But now they are gone…

Not really.

There is another hook called useEffect and we can use it instead of the lifecycles. In other words, we can handle side effects in our applications with hooks. (What is a side effect?)

Here is an example of a component that uses familiar lifecycles:

Old method

import React from 'react';

class TitleMaster extends React.Component {
	constructor(props){
		super(props);
		this.state = {
			title: 'Tuna'
		};
	}
	componentDidMount(){
		document.title = this.state.title; // Changes tab title
	}
	componentDidUpdate(){
		document.title = this.state.title;
	}
	updateTitle(value){
		this.setState({title: value});
	}
	
	render(){
		return (
			<div>
		        <button onClick={
		          () => this.updateTitle(this.state.title =='Tuna'?'Donut':'Tuna')
		        }>Update Title</button>
		    </div>
		);
	}
}

Our component in action.

The component is bulky even for a small functionality. Code in componentDidMount and componentDidUpdate is repeated.

With hooks

Now let’s create the same component with hooks!

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

function TitleMaster(){
  const [title, updateTitle] = useState('Tuna');
  useEffect(() => {
    document.title = title;
  });
  return (
      <div>
          <button onClick={
            () => updateTitle(title =='Tuna'?'Donut':'Tuna')
          }>Update Title</button>
      </div>
    );
}

export default TitleMaster;

Much cleaner. Less code. Easier to understand.

As we mentioned before, a hook is a function. useEffect is also a function that accepts another function and an array. Don’t worry about the array part for now.

So inside the function we pass to useEffect, we can perform our side effect logic. In the example above, we are updating the tab title in the browser. Another common practice is to use data fetching in the useEffect hooks.

We can also use multiple useEffect hooks in one component.

Note that we are placing the hooks inside of our component functions. That way they will have access to the state variables.

Infinite loop

By default useEffect re-renders every time the component changes. Sometime incorrectly implementing the hook might cause infinite loop issue. Remember, we said that the useEffect takes 2 arguments? So the second argument is an object or array of values. Which tells React, “Hey React, re-run the useEffect only when these state values change

const [user, setUser] = userState();
useEffect(() => {
	document.title = user.id;
}, [user.id]); // Re-render the hook Only when user.id changes

Cleanup logic

useEffect hook can also handle cleanup logic. What does that mean? Well, sometimes we subscribe to some APIs and once we are done, we need to unsubscribe from it to prevent any leaks. Or when we create an eventListener, we need a way to remove it.

useEffect can do it by returning a function.

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

function TitleMaster(){
  const [title, updateTitle] = useState('Tuna');
  useEffect(() => {
    document.title = title;
    return () => {
		// Perform clean up logic here
	}
  });
  return (
      <div>
          <button onClick={
            () => updateTitle(title =='Tuna'?'Donut':'Tuna')
          }>Update Title</button>
      </div>
    );
}

TLDR:

React hooks are special functions that can be used in stateless components. They allow us to hook into react features and add state management to the components. There are 3 main hooks: useState, useEffect and useContextuseState can replaces the current way of declaring state object in the constructor and manipulating values in it by using this.setState()useEffect can be used instead of react lifecycles. These hooks are not meant to replace the current way of creating components. They are backwards compatible. No need to rewrite your existing components using hooks. They can make your projects much cleaner by letting you write less code though.

Part 2: What are React Hooks and why you should care about them

In Part 2 we will cover the following:

  • Context Hook
  • How to make custom hooks!
  • Important Rules with Hooks
  • Real life application example