React Hooks Tutorial for Beginners: Getting Started With React Hooks

React Hooks Tutorial for Beginners: Getting Started With React Hooks

React hooks tutorial for beginners: learn React hooks step by step, with a look at how the same logic would be implemented with ES6 classes. In this React hooks tutorial you will learn how to use React hooks, what they are, and why we’re doing it!

Learn React hooks step by step, with a look at how the same logic would be implemented with ES6 classes. In this React hooks tutorial you will learn how to use React hooks, what they are, and why we’re doing it!

Here I am, writing a React hooks tutorial for you. I decided to wait until hooks got finally released before dropping this post. Together we’ll learn React hooks step by step, with a look at how the same logic would be implemented with ES6 classes.

Enjoy the reading!

React Hooks Tutorial for Beginners: what you will learn

In the following tutorial you’ll learn:

  • how to use React hooks
  • how the same logic would be implemented in React class components
React Hooks Tutorial for Beginners: requirements

To follow along with the tutorial you should have a basic understanding of:

  • how to use React hooks
  • how the same logic would be implemented in React class components
React Hooks Tutorial for Beginners: setting up the project

If you want to follow along with the examples make sure to configure a React development environment. Run:

npx create-react-app exploring-hooks


and you’re good to go!

(You should have one of the latest version of Node.js for running npx).

React Hooks Tutorial for Beginners: in the beginning there was setState

I won’t go too deep here, I assume you’re already using React in your project but let me do a quick recap.

React is a library for building user interfaces and one of its perks is that the library itself imposes a strict data flow to the developer. Do you remember jQuery? With jQuery it’s almost impossible to clearly structure a project, let alone defining how the data should flow across the UI. It’s hard to keep track of what function is changing what piece of UI.

The same applies to plain JavaScript: even if with self-disclipine and practice it’s possibile to come up with a well structured project (thinking about the module pattern), good luck tracking state and interactions between functions (it can be done but it’s hard without external help, see Redux).

These problems has been somewhat eased by React: by enforcing a clear structure (container and functional components) and a strict data flow (components react to state and props change) now its easier than before to create well reasoned UI logic.

So the theory in React is that a piece of UI can “react” in response to a state change. The basic form for expressing this flow was an ES6 class up until now. Consider the following example, an ES6 class extending from React.Component, with an internal state:

import React, { Component } from "react";

export default class Button extends Component {
  constructor() {
    super();
    this.state = { buttonText: "Click me, please" };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  }

  render() {
    const { buttonText } = this.state;
    return <button onClick={this.handleClick}>{buttonText}</button>;
  }
}


As you can see from the code above the component’s internal state gets mutated by setState when clicking the button. The text’s button in turns reacts to this change and gets the updated text.

A more concise version of the component can be expressed by removing the constructor and leveraging the ECMAScript class field proposal:

import React, { Component } from "react";

export default class Button extends Component {
  state = { buttonText: "Click me, please" };

  handleClick = () => {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  };

  render() {
    const { buttonText } = this.state;
    return <button onClick={this.handleClick}>{buttonText}</button>;
  }
}


So, in the beginning there was setState (and still it will be). But keep calm. The style above is perfectly fine and ES6 classes in React won’t go away anytime soon.

But now with React hooks it’s possible to express the flow internal state change -> UI reaction without using an ES6 class.

Follow me into the next section …

React Hooks Tutorial for Beginners: updating the state in React … without setState

So what options do we have for managing the internal state in React now that setState and classes are not a need anymore?

Enter the first and most important React hook: useState. useState is a function exposed by the react package. You will import that function at the top of your files as:

import React, { useState } from "react";


By importing useState in your code you’re signaling the intent to hold some kind of state inside your React component. And more important, that React component shouldn’t be an ES6 class anymore. It can be a pure and simple JavaScript function. This is the most appealing thing of this hooks story.

After importing useState you’ll pick an array containing two variables out of useState, and the code should go inside your React component:

const [buttonText, setButtonText] = useState("Click me, please")


Confused by this syntax? It’s ES6 destructuring. The names above can be anything you want, it doesn’t matter for React. Anyway I advise using descriptive and meaningful variable names depending on the state’s purpose.

The argument passed to useState is the actual starting state, the data that will be subject to changes. useState returns for you two bindings:

  • how to use React hooks
  • how the same logic would be implemented in React class components

So the previous example, a button component, with hooks becomes:

import React, { useState } from "react";

export default function Button() {
  const [buttonText, setButtonText] = useState("Click me, please");

  return (
    <button onClick={() => setButtonText("Thanks, been clicked!")}>
      {buttonText}
    </button>
  );
}


For calling the setButtonText state updater inside the onClick handler you can use an inline arrow function. But if you prefer using a regular function you can do:

import React, { useState } from "react";

export default function Button() {
  const [buttonText, setButtonText] = useState("Click me, please");

  function handleClick() {
    return setButtonText("Thanks, been clicked!");
  }

  return <button onClick={handleClick}>{buttonText}</button>;
}


Must be honest, I fancy regular functions more than arrow functions, unless I have specific requirements. Readability improves a lot. Also, when I write code I think always of the next developer that will mantain that code. And my code should be readable.

React hooks, that’s it! I could end this post here but not before showing you how to fetch data with hooks.

Head over the next section!

React Hooks Tutorial for Beginners: in the beginning there was componentDidMount (and render props)

Data fetching in React! Do you remember the old days of componentDidMount? You would slap fetch(url) in componentDidMount and call it a day. Here’s how to fetch an array of data from an API for rendering out a nice list:

import React, { Component } from "react";

export default class DataLoader extends Component {
  state = { data: [] };

  componentDidMount() {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data =>
        this.setState(() => {
          return { data };
        })
      );
  }

  render() {
    return (
      <div>
        <ul>
          {this.state.data.map(el => (
            <li key={el.id}>{el.title}</li>
          ))}
        </ul>
      </div>
    );
  }
}


You could even use async/await in componentDidMount, with some caveats. But most of the asynchronous logic in my projects would live outside React components. There are yet a couple of shortcomings in the above code. The rendered list is fixed but with a render prop we can easily pass children as a function. The refactored component would look like the following:

import React, { Component } from "react";

export default class DataLoader extends Component {
  state = { data: [] };

  componentDidMount() {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data =>
        this.setState(() => {
          return { data };
        })
      );
  }

  render() {
    return this.props.render(this.state.data);
  }
}


And you would consume the component by providing a render prop from the outside:

      <DataLoader
        render={data => {
          return (
            <div>
              <ul>
                {data.map(el => (
                  <li key={el.id}>{el.title}</li>
                ))}
              </ul>
            </div>
          );
        }}
      />


Even this pattern (born for providing a nicer alternative to mixins and HOCs) has its shortcomings. And that’s I guess the exact reason which lead React engineers to come up with hooks: provide a better ergonomics for encapsulating and reusing logic in React.

So impatient as I am, one of the first thing I wanted to try with hooks was data fetching. But what hook I’m supposed to use for fetching data? Would the component still use the render prop pattern?

Let’s see into the next section!

React Hooks Tutorial for Beginners: fetching data with useEffect

I thought data fetching with React hooks shouldn’t look so different from useState. A quick glance at the documentation gave me an hint: useEffect could be the right tool for the job.

I read: “useEffect serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in React classes, but unified into a single API”

Bingo! It’s amazing, isn’t it? With this knowledge in hand I refactored the first version of Dataloader for using useEffect. The component becomes a function and fetch gets called inside useEffect. Moreover, instead of calling this.setState I can use setData (an arbitrary function extracted from useState):

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

export default function DataLoader() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data));
  });

  return (
    <div>
      <ul>
        {data.map(el => (
          <li key={el.id}>{el.title}</li>
        ))}
      </ul>
    </div>
  );
}


At this point I thought “what could be wrong?” and I launched the app. This is what I saw in the console:

It was clearly my fault because I’ve already got an hint of what was going on:

"useEffect serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount"

componentDidUpdate! componentDidUpdate is a lifecycle method running every time a component gets new props, or a state change happens. That’s the trick. If you call useEffect like I did you would see an infinite loop. And for solving this “bug” you would need to pass an empty array as a second argument to useEffect:

//

  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data));
  }, []); // << super important array

//


I wish this info got the visibility it deserves rather than being at the end of this page: Using the Effect Hook. But even with this informations I wouldn’t suggest rewriting all your React components to use hooks for fetching. A lot could still change in the near future, as Ryan Florence suggests:

Anyway, useEffect replaces componentDidMount, componentDidUpdate, and componentWillUnmount, which I think is a nice thing for both experts developers and newcomers to React.

React Hooks Tutorial for Beginners: can I use render props with React hooks?

Of course! But there’s no point in doing that. Our DataLoader component becomes:

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

export default function DataLoader(props) {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data));
  }, []); // << super important array

  return props.render(data)
}


And you would consume the component by providing a render prop from the outside as we did in the previous example.

But again, there’s no point in this refactoring because React hooks were born for a reason: sharing logic between components, and we’ll see an example in the next section.

React Hooks Tutorial for Beginners: your first custom React hook

Instead of HOCs and render props, we can encapsulate our logic in a React hook and then import that hook whenever we feel the need. In our example we can create a custom hooks for fetching data.

A custom hook is a JavaScript function whose name starts with “use”, according to the React documentation. Easier done than said. Let’s make a useFetch hook then:

// useFetch.js
import { useState, useEffect } from "react";

export default function useFetch(url) {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => setData(data));
  }, []);

  return data;
}


And this is how you would use the custom hook:

import React from "react";
import useFetch from "./useFetch";

export default function DataLoader(props) {
  const data = useFetch("http://localhost:3001/links/");
  return (
    <div>
      <ul>
        {data.map(el => (
          <li key={el.id}>{el.title}</li>
        ))}
      </ul>
    </div>
  );
}


This is what make hooks so appealing: finally we have a nice, standardized, and clean way for encapsulating and sharing logic.

NOTE: I didn’t account for fetch errors in the code above, do your homeworks!

React Hooks Tutorial for Beginners: can I use async/await with useEffect?

When playing with useEffect I wanted to try async/await inside the hook. Let’s see our custom hook for a moment:

// useFetch.js
import { useState, useEffect } from "react";

export default function useFetch(url) {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => setData(data));
  }, []);

  return data;
}


For refactoring to async/await the most natural thing you would do is probably:

// useFetch.js
import { useState, useEffect } from "react";

export default function useFetch(url) {
  const [data, setData] = useState([]);

  useEffect(async () => {
    const response = await fetch(url);
    const data = await response.json();
    setData(data);
  }, []);

  return data;
}


Makes sense right? Then I opened the console and React was screaming at me:

“Warning: An Effect function must not return anything besides a function, which is used for clean-up.” Followed by a complete explanation of what I was doing wrong. How nice!

Turns out you cannot return a Promise from useEffect. JavaScript async functions always return a promise and useEffect should exclusively return another function, which is used for cleaning up the effect. That is, if you were to start setInterval in useEffect you would return a function (we have a closure there) for clearing up the interval.

So for making React happy we could rewrite our asynchronous logic like so:

// useFetch.js
import { useState, useEffect } from "react";

export default function useFetch(url) {
  const [data, setData] = useState([]);

  async function getData() {
    const response = await fetch(url);
    const data = await response.json();
    setData(data);
  }

  useEffect(() => {
    getData();
  }, []);

  return data;
}


and your custom hook will work again.

React Hooks Tutorial for Beginners: wrapping up

React hooks are a nice addition to the library. Born as an RFC in November 2018 they caught up quickly and landed in React 16.8. Think of React hooks as encapsulated states living outside your React components.

React hooks make render props and HOCs almost obsolete and provide a nicer ergonomics for sharing logic. With React hooks you can reuse common pieces of logic between React components.

React ships with a bunch of pre-defined hooks. The most important are useState and useEffect. useState makes possible to use local state inside React components, without resorting to ES6 classes.

useEffect replaces componentDidMount, componentDidUpdate, and componentWillUnmount providing a unified API. There are a lot of other hooks and I suggest reading through the official documentation for learning more.

It’s easy to foresee where React is going: functional components all over the place! But even then we will have 3 ways for expressing components in React:

  • how to use React hooks
  • how the same logic would be implemented in React class components

I can see a lot of convenience in hooks and I’m happy with the API they provide. It’s amazing how React is evolving, the community seems to find always a clever way for solving harder problems.

React Hooks Tutorial for Beginners: resources for learning React hooks

There are a lot of resources out there for learning about React hooks and admittedly some are better than this post. So here are my suggestions.

The React documentation is your first stop for learning hooks: Introducing Hooks is an high level overview of how and why hooks are here. Hooks at a glance goes a bit deeper and it’s the starting point for understanding hooks in depth.

Tania Rascia has a nice introduction on hooks with Build a CRUD App in React with Hooks. Speaking of more advanced use cases Matt Hamlin as a nice write up on useReducer, another React hooks for managing state changes.

Funnily enough useReducer resembles a lot Redux reducers. That’s a proof of how influential Redux is in the React community (that shouldn’t be a surprise since Dan Abramov is behind both Redux and React). I highly suggest learning Redux if you haven’t done yet, it will help a lot before studying useReducer.

React Hooks Tutorial for Beginners: appendix

At the beginning of the article I said: “With jQuery it’s almost impossible to clearly structure a project, let alone defining how the data should flow across the UI”.

And:

“The same applies to plain JavaScript: even if with self-disclipine and practice it’s possibile to come up with a well structured project, good luck tracking application’s state.”

But you might not need React for building user interfaces. Sometimes I build projects with vanilla JavaScript and here I am, doing fine. I use to create a simple prototype without any JavaScript library when I’m not sure what shape the project will take. In these kind of projects I rely on the module pattern for organising the code.

Being able to properly organise and document your code, even with vanilla JavaScript is a valuable asset for every JavaScript developer. For learning more about the module pattern in JavaScript I suggest reading Mastering the module pattern by Todd Motto and JavaScript design patterns by Addy Osmani.

Tracking state changes in the UI is really hard on the other hand. And for this kind of job a lot of libraries had born and die. One of my favorite is Redux and it can be used even with vanilla JavaScript.

Thanks for reading! See you next time!

How to perform CRUD operations and consume RESTful API using React?

How to perform CRUD operations and consume RESTful API using React?

In this post, we go through a tutorial on how to create a React application that can perform CRUD functions and take in data from a RESTful API. Recently we have experienced rapid growth of mobile, Internet-based communication. We have...

In this post, we go through a tutorial on how to create a React application that can perform CRUD functions and take in data from a RESTful API.

Recently we have experienced rapid growth of mobile, Internet-based communication. We have smartphones, tablets, netbooks, and other connected devices that create a need to serve appropriate content for each specific front-end profile. Also, the Internet is becoming available for new regions and social groups, constantly increasing web traffic. On the other side, users' computers and browsers, and JavaScript itself are getting more and more powerful, providing more possibilities for processing data on the web via the client-side. In this situation, the best solution can often be to send data as the response, instead of sending page content. That is, we don't need to reload the whole page on each request, but send back the corresponding data and let the client (front-end stuff) process the data.

We can develop a backend application exposing a remote API (usually based on the REST protocol) and a front-end (usually JavaScript) application, which communicates with the API and renders all the data on the device.

If backend data is consumed by humans, we need to develop a user interface (UI) to provide the possibility for users to manage the data. Modern web applications should have responsive and friendly UIs, ensuring adequate user experience. Also, modern UIs can be arbitrarily complex, with multi-panel, nested layouts, paging, progress bars, etc. In this case, the component model can be the right solution. React.js is a light-weight JavaScript framework, which is oriented toward the creation of component-based web UIs. React doesn't provide any means for communicating with the backend, but we can use any communication library from inside React components.

Before starting development, we need to set up a React.js development environment.

1. React.js Development Environment Set Up

There are several ways to use React.js. The simplest way is just to include React libraries in the

Learn React.js - Full Course for Beginners

Learn React.js - Full Course for Beginners

Learn the complete React-Redux front end system: React.js, Redux, React Router, React Hooks and Auth0

Other courses have made partial updates to older techniques and code, but this course is built from the ground up to include the most latest code and techniques from 2019+. You are getting the complete integrated modern techniques and code for React and Redux that are used in development today.

What you'll learn:

  • The Most Modern Version of React and Redux
  • Modern Authentication and Routing Patterns
  • The Knowledge and Skills to Apply to Front End Jobs
  • How React Works Under the Hood
  • The Knowledge to know how different Front End Technologies work together (enough knowledge to impress other engineers)
  • Modern ES5, ES6, ES7 Javascript Syntax.

React-Redux with TypeScript

React-Redux with TypeScript

TypeScript is a typed superset of JavaScript. It has become popular recently in applications due to the benefits it can bring. If you are new to TypeScript it is highly recommended to become familiar with it first before proceeding.

TypeScript is a typed superset of JavaScript. It has become popular recently in applications due to the benefits it can bring. If you are new to TypeScript it is highly recommended to become familiar with it first before proceeding.

TypeScript is a great language to choose if you are a JavaScript developer and interested in moving towards a statically typed language. Using TypeScript is such a logical move for developers that are comfortable with JavaScript but haven’t written in languages that are statically typed (like C, JVM’s, Go, etc).

As I began my journey to TypeScript, (I’m most comfortable with JS, but have written a little bit in Go and C), I found it to be pretty easy to pick up. My initial thought was: “It really isn’t that bad typing all the argument in my function and typing the return value; what’s the fuss all about?” It was nice and simple until we had a project where we needed to create a React/Redux app in TypeScript.

It’s super easy to find material for React + JS, but as you begin to search for React + TS and especially React + Redux + TS, the amount of online tutorials (including YouTube videos) begin to dwindle significantly. I found myself scouring Medium, Stack Overflow, etc. for anything I could find to help explain how to set up the project, how types are flowing between files (especially once Redux is involved), and how to build with Webpack. This article is a way for me to solidify my knowledge of React + Redux + TS, and to hopefully provide some guidance for anyone else who is interested in using this tech stack for the front end. TypeScript is becoming more popular, so I hope this is useful to others in the community.

Prerequisites: I assume you’re aware of how React, Redux, and Webpack work, and also most concepts in TypeScript (at least interfaces and generics).

What are we gonna build? Just to keep it simple, we’ll build the infamous to-do list application. Remember that the purpose is to understand how to set up the project and know how TypeScript integrates with React and Redux. The features that this application will support are:

  1. Add a new item to a list.
  2. Remove an item from the list.

The code for the project can be found here: https://github.com/sterlingdeng/react-redux-ts-boilerplate.

For my project, I didn’t use create-react-app --typescript to get the project started. I found that it was a valuable learning experience to get it started from scratch. I’ll go step by step through the import files and folders needed to get this project up and running. Before we start, let me show you what the final structure looks like.

TS-Redux-React-Boilerplate
├── build
├── node_modules
├── public
│   ├── bundle.js
│   └── index.html
├── src
│   ├── App.tsx
│   ├── index.tsx
│   ├── actions
│   │   ├── actions.ts
│   │   └── index.ts
│   ├── components
│   │   ├── index.ts
│   │   └── TodoItem.tsx
│   ├── containers
│   │   └── TodoContainer.tsx
│   ├── reducers
│   │   ├── index.ts
│   │   └── todoReducers.ts
│   ├── store
│   │   └── store.ts
│   └── types
│       └── types.d.ts
├── .gitignore
├── package-lock.json
├── package.json
├── tslint.json
├── tsconfig.json
├── webpack.config.js
└── README.md

First, let’s look at the package.json file and install these dependencies using npm i.

"dependencies": {
    "@types/node": "^12.0.0",
    "@types/react": "^16.8.15",
    "@types/react-dom": "^16.8.4",
    "@types/react-redux": "^7.0.8",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-redux": "^7.0.3",
    "redux": "^4.0.1",
    "ts-loader": "^5.4.5",
    "typesafe-actions": "^4.2.0",
    "typescript": "^3.4.5",
    "webpack": "^4.30.0",
    "webpack-cli": "^3.3.1"
  }

Let’s first look at the dependencies with the format @types/[npm module here]. If you aren’t familiar with what these modules are, look up https://github.com/DefinitelyTyped/DefinitelyTyped. Since most modules are written in JavaScript, they aren’t written with proper type definitions. Once you attempt to import a module without any type information into a project that is all typed, TypeScript will complain. To prevent this, the community of contributors at DefinitelyTyped create high-quality type definition of the most commonly used JavaScript modules so that those modules will integrate as seamlessly as possible with TS.

You are probably familiar with the next four. ts-loader is needed because Webpack needs a plugin to parse .ts and .tsx files. (This is similar to babel.)

typesafe-actions is a library I use with Redux + TypeScript. Without it, the files can get quite noisy in terms of declaring types for the Store, Reducer, and Actions. This library provides methods that infer type definition for redux code so that the files are a bit cleaner and focused.

webpack and webpack-cli are used to bundle the .ts and .tsx files into one.js file that can be sent to the front end.

Next, let’s look at thetsconfig.json file. The purpose of this file is to configure how you want the ts compiler to run.

{
  "compilerOptions": {
    "baseUrl": ".",
    "outDir": "build/dist",
    "module": "commonjs",
    "target": "es5",
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react"
  }
}

baseUrl denotes the folder. outDir directs the compiler where it should put the compiled code. module tells the compiler which JavaScript module types to use. target tells the compiler which JS version to target. sourceMap tells the compiler to create a bundle.js.map along with bundle.js. Because bundling will turn multiple files into one large .js file, troubleshooting code will be difficult because you wouldn’t easily know which file and at which line the code failed (since everything is just one big file). The .map file will map the bundled file to the respective unbundled file.

tslint.json provides options on how strict or loose you want the ts linter to be. The various options you can set for the linter can be found online.

Action Creators

Normally when I start projects with Redux, I begin at the action creators. Let’s quickly review the features that we need to implement: Adding an item to a list and removing an item from the list. This means we’ll need two action creators, one for adding and another for removing.

import { action } from "typesafe-actions";

// use typescript enum rather than action constants
export enum actionTypes {
  ADD = "ADD",
  DELETE = "DELETE"
}

export const todoActions = {
  add: (item: string) => action(actionTypes.ADD, item),
  delete: (idx: number) => action(actionTypes.DELETE, idx)
};

In the actions.ts file, I’m using the enum feature in TS to create the action constants. Secondly, I’m using the action method provided from the typesafe-actions module. The first argument you pass into the method is a string which represents that action, and the second argument is the payload. The add method will add an item to the list of to-dos, and the deletemethod will remove an item, based on the provided index, from the list of to-dos.

In terms of type safety, what we want in our reducers file is the proper type of the payload, given a specific action. The feature in TypeScript that provides this support is called discriminated union and type guarding. Consider the example below:

// this is an example of discriminated unions
// this file isn't used in the project

interface ActionAdd {
  type: "ADD",
  payload: string
}

interface ActionDelete {
  type: "DELETE",
  payload: number
}

type Actions = ActionAdd | ActionDelete

function reducer(a: Actions) {
  switch(a.type) {
    case "ADD" : {
      // payload is a string
    }
    case "DELETE" : {
      // payload is a number
    }
  }
}

Given the shape of the two action objects, we can discriminate between them based on the type property. Using control flow analysis, like if-else or switch-case statements, it’s very logical that in line 16, the only type that the payload can be is a string. Since we only have two actions, the remaining payload in line 22 will be a number. If you are interested in learning more about discriminated unions and type guarding, I would recommend learning more about it here and here.

Reducer

After defining our action creators, let’s create the reducer for this project.

import * as MyTypes from "MyTypes";
import { actionTypes } from "../actions/";

interface ITodoModel {
  count: number;
  list: string[];
}

export const initialState: ITodoModel = {
  count: 2,
  list: ["Do the laundry", "Do the dishes"]
};

export const todoReducer = (state: ITodoModel = initialState, action: MyTypes.RootAction) => {
  switch (action.type) {
    case actionTypes.ADD: {
      return {
        ...state,
        count: state.count + 1,
        list: [...state.list, action.payload]
      };
    }
    case actionTypes.DELETE: {
      const oldList = [...state.list];
      oldList.splice(action.payload, 1);
      const newList = oldList;

      return {
        ...state,
        count: state.count - 1,
        list: newList
      };
    }
    default:
      return state;
  }
};

In lines four through seven, I’m defining the model (or schema) of our Todostore. It will keep track of how many to-do items we have, as well as the array of strings. Lines nine through 12 are the initial state when the application first starts. Within the todoReducer function, we want type safety within the casestatements. Based on the earlier gist, we accomplished that by discriminated unions and type guarding, done by typing the action parameter. We have to first define an interface for every action object, and then create a union of them all and assign that to a type. This can get tedious if we have a lot of action creators — luckily, typesafe-actions has methods to help create the proper typing of the action creators without having to actually write out all the interfaces.

declare module "MyTypes" {
  import { StateType, ActionType } from "typesafe-actions";
  // 1 for reducer, 1 for action creators
  export type ReducerState = StateType<typeof import("../reducers").default>;
  export type RootAction = ActionType<typeof import("../actions/actions")>;
}

Ignoring line four for now and focusing on line five, we use a method called ActionType from the module, and import the actions from actions.ts to create the discriminated union types, which are then assigned to a type called RootAction. In line one of the todoReducers.ts, we import MyTypes and in line 14, we type the action parameter with MyTypes.RootAction. This allows us to have IntelliSense and autocompletion within the reducers!

Now that we have the reducer set up, the ReducerState type from the types.d.ts file allows TypeScript to infer the shape of the state object in the reducer function. This will provide IntelliSense when we try to access the payload object within the Reducer. An example of what that looks like is in the picture below.

IntelliSense in the Reducer

Redux Store

Finally, let’s hook up the reducer to the redux store.

import { createStore } from "redux";
import rootReducer from "../reducers";

const store = createStore(rootReducer);

export default store;

Let’s recap what we have accomplished up until this point. We have created and typed our action creators using the action method from typesafe-actions. We have created our types.d.ts file which provide type information on our action creators and reducer state. The reducer has been created and the actions are typed by using MyTypes.RootAction, which provide invaluable auto-completion information of the payload within the reducer’s case statements. And lastly, we created our Redux store.

React and TS

Let’s change gears and begin working on creating and typing our React components. I’ll go over examples of how to properly type both function and class based components, along with instructions on how to type both the props and state (for stateful components).

App.tsx

import * as React from "react";
import TodoContainer from "./containers/TodoContainer";

export const App: React.FC<{}> = () => {
  return (
    <>
      <h1>React Redux Typescript</h1>
      <TodoContainer />
    </>
  );
};

App is a functional component that is typed by writing const App: React.FC<{}>. (FC refers to functional component.) If you aren’t familiar with generics (which is the <{}> ), I think of them like variables but for types. Since the shape of props and state can differ based on different use cases, generics are a way for us to, well, make the component generic! In this case, App doesn’t take any props; therefore, we pass in an empty object as the generic. How do we know that the generic is specifically for props? If you use VS code, IntelliSense will let you know what type it needs.

Where it says <P = {}> , it means type {} has been assigned to P, where Pstands for props. For class-based components, React will use S to refer to state. App is a functional component that receives no props and is not connected to the Redux store. Let’s go for something a little more complicated.

TodoContainer.tsx

import * as React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import * as MyTypes from "MyTypes";
import { actionTypes } from "../actions";
import { TodoItem } from "../components";

interface TodoContainerState {
  todoInput: string;
}

interface TodoContainerProps {
  count: number;
  todoList: string[];
  addToDo: (item: string) => object;
  deleteToDo: (idx: number) => object;
}

class TodoContainer extends React.Component<TodoContainerProps, TodoContainerState> {
  constructor(props) {
    super(props);
    this.state = {
      todoInput: ""
    };
  }

  handleTextChange = e => {
    this.setState({
      todoInput: e.target.value
    });
  };

  handleButtonClick = () => {
    this.props.addToDo(this.state.todoInput);
    this.setState({
      todoInput: ""
    });
  };

  handleDeleteButtonClick = (idx: number) => {
    console.log("deleting", idx);
    this.props.deleteToDo(idx);
  };

  render() {
    let todoJSX: JSX.Element[] | JSX.Element;
    if (!this.props.todoList.length) {
      todoJSX = <p>No to do</p>;
    } else {
      todoJSX = this.props.todoList.map((item, idx) => {
        return (
          <TodoItem item={item} key={idx} idx={idx} handleDelete={this.handleDeleteButtonClick} />
        );
      });
    }

    return (
      <div>
        {todoJSX}
        <input
          onChange={this.handleTextChange}
          placeholder={"New To Do Here"}
          value={this.state.todoInput}
        />
        <button onClick={this.handleButtonClick}>Add To Do</button>
      </div>
    );
  }
}

const MapStateToProps = (store: MyTypes.ReducerState) => {
  return {
    count: store.todo.count,
    todoList: store.todo.list
  };
};

const MapDispatchToProps = (dispatch: Dispatch<MyTypes.RootAction>) => ({
  addToDo: (item: string) => dispatch({ type: actionTypes.ADD, payload: item }),
  deleteToDo: (idx: number) => dispatch({ type: actionTypes.DELETE, payload: idx })
});

export default connect(
  MapStateToProps,
  MapDispatchToProps
)(TodoContainer);

OK, TodoContainer.tsx is the most complicated of them all, but I’ll walk you through what’s going on in the code. TodoContainer is a React Class Component because I need it to hold in its state the value for the input box. It is also connected to the redux store, so it’ll have MapStateToProps and MapDispatchToProps . First, I’ve definedTodoContainerState . Since I’ll be holding the value of the input box in state, I’ll type the property as a string.

Next, I’ve defined TodoContainerProps, which will be the shape of the Container’s props.

Because class-based components can have both state and props, we should expect that there should be at least two generics that we need to pass into React.Component.

P for Props and S for State

If you mouse over React.Component, you can see that it takes in three generics, P, S, and SS. The first two generics are props and state. I’m not quite sure whatSS is and what the use case is. If anyone knows, please let me know in the comments below.

After passing in the generics into React.Component , IntelliSense and autocompletion will work within this.state and for props.

Next, we want to type MapStateToProps and MapDispatchToProps. This is easily achievable by leveraging the MyTypes module that we built in the redux section. For MapStateToProps, we assign the store type to be MyTypes.ReducerState. An example of the IntelliSense it will provide is in the below screenshot.

IntelliSense for MapStateToProps

Lastly, we want to have type safety within MapDispatchToProps. The benefit that is provided is a type-safe payload given an action type.

Type-safe payloads

In the screenshot above, I purposely typed item as a boolean. Immediately, the TSServer will pick up that the boolean payload within MapDispatchToProps is not correct because it’s expecting the payload to be a string, given that the type is actionTypes.ADD. TodoContainer.tsx has the most going on since it is a class based React component, with both state and props, and is also connected to the store.

Before we wrap up, let’s look at our last component: TodoItem.tsx

TodoItem.tsx

This component is a functional component with props — code below.

import * as React from "react";

interface TodoItemProps {
  item: string;
  idx: number;
  handleDelete: (idx: number) => void;
}

export const TodoItem: React.FC<TodoItemProps> = props => {
  return (
    <span>
      {props.item}
      <button onClick={() => props.handleDelete(props.idx)}>X</button>
    </span>
  );
};

The shape of the props are defined in the interface TodoItemProps. The type information is passed into as a generic in React.FC. Doing so will provide auto-completion for props within the component. Awesome.

Another great feature that TypeScript provides when used with React is IntelliSense for props when rendering React Components within JSX. As an example, if you delete idx:number from TodoItemProps and then you navigate to TodoContainer.tsx, an error will appear at the place where you render <TodoItem />.

Property ‘idx’ does not exist

Because we removed idx from the TodoItemProps interface, TypeScript is letting us know that we have provided an additional prop that it couldn’t find, idx, into the component.

Lastly, let’s build the project using Webpack. In the command line, type npm run build. In the public folder within the root directory, you should see bundle.js alongsideindex.html. Open index.html in any browser and you should see a very simple, unstyled, to-do app.

After webpack build

I hope that I was able to demonstrate the power of TypeScript coupled with React and Redux. It may seem a bit overkill for our simple to-do list app — you just need to imagine the benefit of TS + React + Redux at scale. It will help new developers read the code quicker, provide more confidence in refactoring, and ultimately improve development speed.

If you need more reference and material, I used the following two Git repos to teach myself

Both these repos have proved invaluable for my learning, and I hope they will be the same for you.

Build a React Responsive Navigation Menu with React Hooks and Sass

Build a React Responsive Navigation Menu with React Hooks and Sass

In this video tutorial, we will learn how to build a responsive navbar with React, Reach Router, and Sass.

What Is Responsive Web Design?

Responsive Web design is the approach that suggests that design and development should respond to the user’s behavior and environment based on screen size, platform and orientation.

The practice consists of a mix of flexible grids and layouts, images and an intelligent use of CSS media queries. As the user switches from their laptop to iPad, the website should automatically switch to accommodate for resolution, image size and scripting abilities. One may also have to consider the settings on their devices; if they have a VPN for iOS on their iPad, for example, the website should not block the user’s access to the page. In other words, the website should have the technology to automatically respond to the user’s preferences. This would eliminate the need for a different design and development phase for each new gadget on the market.

Download and Run

git clone https://github.com/syntacticsolutions/react-navigation-menu.git
cd react-navigation-menu

In the project directory, you can run:

npm start

Build a React Responsive Navigation Menu with React Hooks and Sass

Runs the app in the development mode.

Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits.

You will also see any lint errors in the console.

npm test

Launches the test runner in the interactive watch mode.

See the section about running tests for more information.

npm run build

Builds the app for production to the build folder.

It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.

Your app is ready to be deployed!

See the section about deployment for more information.

npm run eject

Note: this is a one-way operation. Once you eject, you can’t go back!

If you aren’t satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project.

Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.

You don’t have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.

Learn More

You can learn more in the Create React App documentation.

To learn React, check out the React documentation.

Code Splitting

This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting

Analyzing the Bundle Size

This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size

Making a Progressive Web App

This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app

Advanced Configuration

This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration

Deployment

This section has moved here: https://facebook.github.io/create-react-app/docs/deployment

npm run build fails to minify

This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

How to build a Chat App with React Native

How to build a Chat App with React Native

In this tutorial, we’ll be looking at how to build a demo chat app in React Native

The following features:

  • Public and private chat rooms
  • Roles and permissions
  • Typing indicators
  • Read receipt
  • File uploads
  • Show online and offline users
Prerequisites

Knowledge of **React **and React Native is required to follow this tutorial.

The following package versions are used. If you encounter any issues in compiling the app, try to use the following:

  • Node 11.2
  • Yarn 1.13
  • React Native 0.59

You’ll also need a Chatkit app instance. I assume you already know how to set it up. If not, then check out the official docs.

Lastly, you’ll need an ngrok account for exposing the server to the internet.

Chat App overview

As mentioned earlier, we will be building a chat app. The first screen that the user will see when they open the app is the Login screen. This is where they enter their username so they can log in. We will be creating the users in the **Chatkit **console later:

Once logged in, the app will show the list of rooms they can enter:

Once they’ve selected a room to enter, they’ll be greeted by the Chat app screen:

From the Chat screen, when a user clicks on the avatar of another member of a room, they can see the last message they have seen:

The list of members can be viewed when they click on the Users button in the header. The circle right beside the username indicates their online status:

Aside from those, the app will also have a file attachment and typing indicator feature. Image, audio, and video files can also be previewed.

You can find the code in this GitHub repo.

Setting up roles and permissions

Before we proceed, we first have to set up the roles and permissions for each user in the Chatkit console. We will set up seven users:

Three rooms will also be needed:

  • company
  • web_dev
  • mobile_dev

Everyone will be able to have chat in the company room. While the web and mobile users can only chat within their respective rooms. This can be implemented by means of roles and permissions:

For this app, we only have two roles. These will only be scoped in a specific room:

  • room-admin - check all the permissions.
  • room-member - check all the permissions except room:delete, room:update, room:join, room:leave, room:members:add, room:members:remove.

The only difference between the two is that the room-admin can add or remove users from the room. Though we won’t really be implementing it in the app.

Once you’ve created the roles, the next step is to assign the roles to each of the users. Web Admin and Mobile Admin will have the role of room-admin, while the rest of the users will have the role of room-member. Be sure to apply them to the room where they’re supposed to go:

Don’t forget to assign all of the users as a member of the company room. This time, the room admins will have the same role as the members.

Bootstrapping the app

For us to get into the crucial parts of the app quickly, I prepared a GitHub repo which contains some of the boilerplate code (custom components, navigation setup, styling). Clone it and switch to the starter branch:

    git clone https://github.com/anchetaWern/RNChatkitDemo
    git checkout starter

Whenever I mention “starter branch“, it means that I’m referring to existing code.
Next, install all the dependencies and link the relevant packages:

    yarn
    react-native eject
    react-native link react-native-gesture-handler
    react-native link react-native-permissions
    react-native link react-native-document-picker
    react-native link react-native-fs
    react-native link react-native-config
    react-native link react-native-vector-icons
    react-native link rn-fetch-blob

One of the dependencies (React Native Audio Toolkit) doesn’t play nice with automatic linking. Be sure to check out their docs for information on how to set it up.

Next, we need to update the Android manifest file to allow the app to read from the external storage. This allows us to pick files from the device’s external storage:

    // android/app/src/main/AndroidManifest.xml
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.rnchatkitdemo">
      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
      ...
    </manifest>

Lastly, update the android/app/build.gradle file and include the gradle file for React Native Config:

    apply from: "../../node_modules/react-native/react.gradle"
    apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" // add this

Adding the server code

Now we’re ready to start coding. Let’s kick things off with the server. Some of the code for initializing Chatkit and running the server itself has already been added to the starter branch. All we have to do now is install the dependencies, update the .env file, and add the routes that the app will be consuming.

Start by install the dependencies:

    cd server
    yarn

Next, update the .env file with the Chatkit credentials:

    CHATKIT_INSTANCE_LOCATOR_ID="YOUR CHATKIT INSTANCE LOCATOR ID (v1:us1:)"
    CHATKIT_SECRET_KEY="YOUR CHATKIT SECRET KEY"

Now we can proceed with the code. The /users route allows us to fill the users array with users data that will be used when someone logs in. Later on, be sure to access <a href="http://localhost:5000/users" target="_blank">http://localhost:5000/users</a> on your browser before you try logging in a user. This is because the login code uses the data in the users array to get specific user data:

    // server/index.js
    let users = [];
    app.get("/users", async (req, res) => {
      try {
        users = await chatkit.getUsers();
        res.send({ users });
      } catch (get_users_err) {
        console.log("error getting users: ", get_users_err);
      }
    });

Note: We only have the above route to simplify things. You don’t really want to be exposing user data in a production app.
When a user logs in, the app makes a request to the /user route. This will return the user’s info based on their username:

    app.post("/user", async (req, res) => {
      const { username } = req.body;
      try {
        const user = users.find((usr) => usr.name == username);
        res.send({ user });
      } catch (get_user_err) {
        console.log("error getting user: ", get_user_err);
      }
    });

Next, add the route which returns the rooms that the user (the one supplied in the request body) is a member of. This is the reason why we went through the trouble of assigning each of the users to their rooms:

    app.post("/rooms", async (req, res) => {
      const { user_id } = req.body;
      try {
        const rooms = await chatkit.getUserRooms({
          userId: user_id
        });
        res.send({ rooms });
      } catch (get_rooms_err) {
        console.log("error getting rooms: ", get_rooms_err);
      }
    });

The getUserRooms is preferable to the getJoinable rooms method because the rooms we created (except for the company room) are private. This means they won’t show up when you call getJoinableRooms. Another reason is that the room wouldn’t even be returned if the user is already a member of that room.
Lastly, add the route for returning the user permissions. This will be called when a user enters a specific room so that the app knows what the user can or cannot do in that specific room:

    app.post("/user/permissions", async(req, res) => {
      const { room_id, user_id } = req.body;
      try {
        const roles = await chatkit.getUserRoles({ userId: user_id });
        const role = roles.find(role => role.room_id == room_id);
        const permissions = (role) ? role.permissions : [];
        res.send({ permissions });
      } catch (user_permissions_err) {
        console.log("error getting user permissions: ", user_permissions_err);
      }
    });

The response data in the API call above is an array containing the individual permissions. Here’s an example: ['room:join', 'room:leave'].

Group screen

The Group screen is where the user will be redirected to when they have logged in to the app. We won’t go through the login code as it has already been pre-coded. All you need to know is that the Login screen sends a request to the /user route of the server. As you’ve seen earlier, this returns the user data. From this data, the only thing we want to pass to the Group screen is the user_id:

    // src/screens/Group.js
    state = {
      rooms: []
    };

    constructor(props) {
      super(props);
      const { navigation } = this.props;
      this.user_id = navigation.getParam("id");
    }

When the component is mounted, we send a request to the /rooms route. This returns the list of rooms that the user is a member of:

    async componentDidMount() {
      try {
        const response = await axios.post(`${CHAT_SERVER}/rooms`, { user_id: this.user_id });
        const { rooms } = response.data;
        this.setState({
          rooms
        });
      } catch (get_rooms_err) {
        console.log("error getting rooms: ", get_rooms_err);
      }
    }

Next, render the list of rooms:

    render() {
      const { rooms } = this.state;
      return (
        <View style={styles.container}>
          {
            rooms &&
            <FlatList
              keyExtractor={(item) => item.id.toString()}
              data={rooms}
              renderItem={this.renderRoom}
            />
          }
        </View>
      );
    }

Here’s the code for rendering each list item. It has a button which when clicked by the user redirects them to the room they selected:

    renderRoom = ({ item }) => {
      return (
        <View style={styles.list_item}>
          <Text style={styles.list_item_text}>{item.name}</Text>
          <Button title="Enter" color="#0064e1" onPress={() => {
            this.enterChat(item);
          }} />
        </View>
      );
    }

Here’s the code for entering a chat room. This sends a request to the /user/permissions route which then returns the permissions for that specific room. From there, we determine if the user is a room admin if they have the room:members:add permission. This means that they can add new users to the room:

    enterChat = async (room) => {
      try {
        const response = await axios.post(`${CHAT_SERVER}/user/permissions`, { room_id: room.id, user_id: this.user_id });
        const { permissions } = response.data;
        const is_room_admin = (permissions.indexOf('room:members:add') !== -1);

        this.props.navigation.navigate("Chat", {
          user_id: this.user_id,
          room_id: room.id,
          room_name: room.name,
          is_room_admin
        });

      } catch (get_permissions_err) {
        console.log("error getting permissions: ", get_permissions_err);
      }
    };

We won’t really be implementing the adding of users in this tutorial. This is just to show how permissions can be used to limit what the user can do within the app.##

Chat screen

We can now proceed to the main meat of the app. As we have lots of things to implement, I’ve divided it into multiple sections, each discussing a specific feature of the chat app.

Importing the dependencies

First, we import the dependencies:

    // src/screens/Chat.js
    import React, { Component } from "react";
    import { View, Text, ActivityIndicator, FlatList, TouchableOpacity, Alert } from "react-native";
    import { GiftedChat, Send, Message } from "react-native-gifted-chat"; // for the chat UI
    import { ChatManager, TokenProvider } from "@pusher/chatkit-client"; // for implementing chat functionality
    import axios from "axios"; // for making requests to the server
    import Config from "react-native-config"; // for reading .env file
    import Icon from "react-native-vector-icons/FontAwesome"; // for showing icons
    import { DocumentPicker, DocumentPickerUtil } from "react-native-document-picker"; // for picking files
    import * as mime from "react-native-mime-types"; // for determining the mime type of the picked file
    import Modal from "react-native-modal";
    import RNFetchBlob from "rn-fetch-blob"; // for converting the attached file to a blob
    import RNFS from "react-native-fs"; // for getting the base64 string representation of a file

Chatkit expects the file attachments to be a Blob. React Native doesn’t really have support for this format by default. So we use rn-fetch-blob as a polyfill to implement it. Later on, you’ll see this in action in the Attaching files section:

    const Blob = RNFetchBlob.polyfill.Blob;
    const fs = RNFetchBlob.fs;
    window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
    window.Blob = Blob;

Next, we have a few pre-coded components which are used to render either an audio or video player depending on the format of the attached file. Note that these components only support mp3 files for audio, and mp4 files for video:

    import ChatBubble from "../components/ChatBubble";
    import AudioPlayer from "../components/AudioPlayer";
    import VideoPlayer from "../components/VideoPlayer";

You can read my previous tutorial on playing audio and video file attachments in a Chatkit React Native app if you want to learn more about how those components are implemented
Lastly, update the .env file with your Chatkit config.

Initializing the chat code

In the Chat screen’s header, we add a button for opening the modal for viewing all the members of the room (whether they’re offline or online):

    class Chat extends Component {

      static navigationOptions = ({ navigation }) => {
        const { params } = navigation.state;
        return {
          headerTitle: params.room_name,
          headerRight: (
            <View style={styles.header_right}>
              <TouchableOpacity style={styles.header_button_container} onPress={params.showUsersModal}>
                <View style={styles.header_button}>
                  <Text style={styles.header_button_text}>Users</Text>
                </View>
              </TouchableOpacity>
            </View>

          ),
          headerStyle: {
            backgroundColor: "#333"
          },
          headerTitleStyle: {
            color: "#FFF"
          }
        };
      };

      // next: initialize state

    }

Next, initialize the state:

    state = {
      company_users: null,
      room_users: null,
      messages: [],
      is_initialized: false, // if Chatkit is initialized or not
      is_picking_file: false, // to show/hide the loading animation when picking files
      // to show/hide the various modal windows:
      is_video_modal_visible: false,
      is_last_viewed_message_modal_visible: false,
      is_users_modal_visible: false, 
      is_typing: false, // if there's someone in the room who is currently typing
      typing_user: null, // the username of the user who is typing
      show_load_earlier: false, 
      viewed_user: null, // username of user whose read receipt is currently being viewed
      viewed_message: null // the text message being viewed
    };

    // next: add constructor

Next, get the navigation params that were passed from the Group screen earlier:

    constructor(props) {
      super(props);
      const { navigation } = this.props;

      this.user_id = navigation.getParam("user_id");
      this.room_id = navigation.getParam("room_id");
      this.is_room_admin = navigation.getParam("is_room_admin");

      this.modal_types = {
        video: 'is_video_modal_visible',
        last_viewed_message: 'is_last_viewed_message_modal_visible',
        users: 'is_users_modal_visible'
      }
    }

    // next: add componentDidMount

Once the component is mounted, initialize Chatkit:

    async componentDidMount() {
      this.props.navigation.setParams({
        showUsersModal: this.showUsersModal
      });

      try {
        const chatManager = new ChatManager({
          instanceLocator: CHATKIT_INSTANCE_LOCATOR_ID,
          userId: this.user_id,
          tokenProvider: new TokenProvider({ url: CHATKIT_TOKEN_PROVIDER_ENDPOINT })
        });

        let currentUser = await chatManager.connect();
        this.currentUser = currentUser;

        // next: subscribe to room

      } catch (chat_mgr_err) {
        console.log("error with chat manager: ", chat_mgr_err);
      }
    } 

We then subscribe to the room. We only need to subscribe to the hooks for receiving messages, and showing/hiding typing indicators:

    await this.currentUser.subscribeToRoomMultipart({
      roomId: this.room_id,
      hooks: {
        onMessage: this.onReceive,
        onUserStartedTyping: this.startTyping,
        onUserStoppedTyping: this.stopTyping
      }
    });

We don’t need to subscribe to the onPresenceChanged hook because the presence data from this.currentUser.users is live data. This means that when the users either offline or online, their presence state also changes, so the UI is automatically updated.
Lastly, we update the state with the room members data:

    await this.setState({
      is_initialized: true,
      room_users: this.currentUser.users
    });

Chat UI

We can now proceed to the UI. Start by extracting the data that we need from the state:

    render() {
      const {
        is_initialized,
        room_users,
        messages,
        video_uri,
        is_video_modal_visible,
        is_last_viewed_message_modal_visible,
        viewed_user,
        viewed_message,
        is_users_modal_visible,
        show_load_earlier,
        typing_user
      } = this.state;

      // next: render the Chat UI
    }

Next, render the chat UI. All the heavy lifting is already done for us by React Native Gifted Chat, so all we have to do is supply all the props that it needs:

    return (
      <View style={styles.container}>
        {(!is_initialized) && (
          <ActivityIndicator
            size="small"
            color="#0064e1"
            style={styles.loader}
          />
        )}

        {is_initialized && (
          <GiftedChat
            messages={messages}
            onSend={messages => this.onSend(messages)} // function to execute when send button is clicked
            user={{
              _id: this.user_id
            }}
            renderActions={this.renderCustomActions} // for rendering button for attaching files
            renderSend={this.renderSend} // custom send button UI
            renderMessage={this.renderMessage} // custom chat bubble UI
            onInputTextChanged={this.onTyping} // function to execute while the user is typing
            renderFooter={this.renderFooter} // for rendering the typing indicator
            extraData={{ typing_user }} // so that the footer will be re-rendered when the typing user is updated
            onPressAvatar={this.viewLastReadMessage} // function to execute when user avatar is clicked

            loadEarlier={show_load_earlier} // for loading earlier messages
            onLoadEarlier={this.loadEarlierMessages}
          />
        )}

        // next: add modals      

      </View>
    );

Next, render the modal for showing the fullscreen version of the attached video files:

    <Modal isVisible={is_video_modal_visible}>
      <View style={styles.modal}>
        <TouchableOpacity onPress={this.hideModal.bind(this, 'video')}>
          <Icon name={"close"} size={20} color={"#565656"} style={styles.close} />
        </TouchableOpacity>
        <VideoPlayer uri={video_uri} />
      </View>
    </Modal>

Next, render the modal for showing the last message viewed by a specific member of the room:

    {
      viewed_user && viewed_message &&
      <Modal isVisible={is_last_viewed_message_modal_visible}>
        <View style={styles.modal}>
          <View style={styles.modal_header}>
            <Text style={styles.modal_header_text}>Last viewed msg: {viewed_user}</Text>
            <TouchableOpacity onPress={this.hideModal.bind(this, 'last_viewed_message')}>
              <Icon name={"close"} size={20} color={"#565656"} style={styles.close} />
            </TouchableOpacity>
          </View>

          <View style={styles.modal_body}>
            <Text>Message: {viewed_message}</Text>
          </View>
        </View>
      </Modal>
    }

Next, render the modal for showing the list of members of the room:

    {
      room_users &&
      <Modal isVisible={is_users_modal_visible}>
        <View style={styles.modal}>
          <View style={styles.modal_header}>
            <Text style={styles.modal_header_text}>Users</Text>
            <TouchableOpacity onPress={this.hideModal.bind(this, 'users')}>
              <Icon name={"close"} size={20} color={"#565656"} style={styles.close} />
            </TouchableOpacity>
          </View>

          <View style={styles.modal_body}>
            <FlatList
              keyExtractor={item => item.id.toString()}
              data={room_users}
              renderItem={this.renderUser}
            />
          </View>
        </View>
      </Modal>
    }

Here’s the function for rendering each individual user. This will show a green circle next to the user’s name if they are online, and a gray circle if they’re offline:

    renderUser = ({ item }) => {
      const online_status = item.presenceStore[item.id];

      return (
        <View style={styles.list_item_body}>
          <View style={styles.list_item}>
            <View style={[styles.status_indicator, styles[online_status]]}></View>
            <Text style={styles.list_item_text}>{item.name}</Text>
          </View>
        </View>
      );
    }

Here’s the code for showing the users modal:

    showUsersModal = () => {
      this.setState({
        is_users_modal_visible: true
      });
    }

Lastly, here’s the code for hiding a specific modal:

    hideModal = (type) => {
      const modal = this.modal_types[type];
      this.setState({
        [modal]: false
      });
    }

Attaching files

The code for rendering the custom button for attaching files is already in the starter branch, so all we have to do is add the code for actually attaching a file. To implement this, we use the React Native Document Picker package. We call the DocumentPicker.show function to show the default file picker of the device’s operating system. We use the DocumentPickerUtil.allFiles function to specify that all file types can be selected by the user. You can also supply a specific file type if you want. From their source code, you can see that those methods simply return a mime-type wild card. Though we will only have previews for png, jpg, mp3, and mp4 files:

    openFilePicker = async () => {
      await this.setState({
        is_picking_file: true // show the loader instead of the button for picking files to prevent the user from clicking it again
      });

      DocumentPicker.show({
        filetype: [DocumentPickerUtil.allFiles()],
      }, async (err, file) => {
        if (!err) { // if there's no error in picking the file

          // next: convert the file to a blob
        }

        this.setState({
          is_picking_file: false
        });
      });
    }

Next, we determine the mime type of the file and convert it to its base64 string representation using the react-native-fs package. From there, we convert it to a Blob using the rn-fetch-blob package. As you learned earlier, this acts as a polyfill for implementing Blobs in the React Native environment. Lastly, we temporarily store the name, type, and the Blob representation of the file so that we can easily get it later when we send a message:

    try {
      const file_type = mime.contentType(file.fileName);
      const base64 = await RNFS.readFile(file.uri, "base64");

      const file_blob = await Blob.build(base64, { type: `${file_type};BASE64` });

      this.attachment = {
        file_blob: file_blob,
        file_name: file.fileName,
        file_type: file_type
      };

      Alert.alert("Success", "File attached!");

    } catch (attach_err) {
      console.log("error attaching file: ", attach_err);
    }

Sending messages

The code for sending messages is already included in the starter branch. But we need to update it so that it sends multi-part messages (text with attachment). Replace the existing onSend method with the following. The main difference here is that the text message is included in the message_parts array. This allows us to supply different kinds of content to an individual message. In this case, the other type is an attachment. If the user has selected an attachment, we push it into the message_parts array:

    onSend = async ([message]) => {
      let message_parts = [
        { type: "text/plain", content: message.text }
      ];

      if (this.attachment) {
        const { file_blob, file_name, file_type } = this.attachment;
        message_parts.push({
          file: file_blob, // the file Blob
          name: file_name, // the file name
          type: file_type // the file type
        });
      }

      this.setState({
        is_sending: true // show the loading animation for sending a message
      });

      // next: send message
    }

Next, we send the message. The main difference here is that we’re now using the sendMultipartMessage method instead of the sendSimpleMessage method. Another important thing to note is that when a user sends a message, we also assume that they’ve already read the last message they received. So we set a read cursor using the ID of the last message they received:

    try {
      if (this.last_message_id) {
        const set_cursor_response = await this.currentUser.setReadCursor({
          roomId: this.room_id,
          position: this.last_message_id // the ID of the last message they received
        });
      }
      // send the message
      await this.currentUser.sendMultipartMessage({
        roomId: this.room_id,
        parts: message_parts
      });

      this.attachment = null;
      await this.setState({
        is_sending: false // hide the loading animation
      });
    } catch (send_msg_err) {
      console.log("error sending message: ", send_msg_err);
    }

Receiving messages

The code for receiving messages has already been included in the starter branch, but we need to update it so it sets the last_message_id that we used in the code for sending a message earlier:

    onReceive = async (data) => {
      this.last_message_id = data.id; // add this
      // ...
    }

Next, replace the existing getMessage function with the following:

    getMessage = async ({ id, sender, parts, createdAt }) => {

      const text = parts.find(part => part.partType === 'inline').payload.content;
      const attachment = parts.find(part => part.partType === 'attachment');

      const attachment_url = (attachment) ? await attachment.payload.url() : null;
      const attachment_type = (attachment) ? attachment.payload.type : null;

      const msg_data = {
        _id: id,
        text: text,
        createdAt: new Date(createdAt),
        user: {
          _id: sender.id,
          name: sender.name,
          avatar: `https://ui-avatars.com/api/?background=d88413&color=FFF&name=${sender.name}`
        }
      };

      if (attachment) {
        Object.assign(msg_data, { attachment: { url: attachment_url, type: attachment_type } });
      }

      if (attachment && attachment_type.indexOf('video') !== -1) {
        Object.assign(msg_data, { video: attachment_url });
      }

      if (attachment && attachment_type.indexOf('image') !== -1) {
        Object.assign(msg_data, { image: attachment_url });
      }

      return {
        message: msg_data
      };
    }

We’re using the UI Avatars API to automatically generate an image which matches the user’s initials.
In the code above, the most important distinction is the way we’re getting the text. In this case, the message is already considered a multipart message instead of a simple plain-text message. The first part you specify should always be the text. While the parts that follow can be a file attachment or a URL. The type of a specific part can either be inline, url, or attachment. In this case, we’re handling both inline and attachment types. If you specify, text/plain as the type for a message part, it’s automatically considered as inline. And if you specify a file to a message part, it’s automatically considered as an attachment.

To get to the actual text of an inline message part, we simply extract payload.content:

    const text = parts.find(part => part.partType === 'inline').payload.content;

But for an attachment message part, you have to call the payload’s url() method in order to get the direct URL to the file. This is what we use for previewing the files:

    const attachment = parts.find(part => part.partType === 'attachment');
    const attachment_url = (attachment) ? await attachment.payload.url() : null;

Next, add the renderMessage function. We don’t have this on the starter branch because we’re only rendering plain text previously. This time though, we need to cater for audio (mp3), and video (mp4) files as well. We only render a custom chat bubble if it’s an audio file because Gifted Chat already has a feature for rendering previews for image and video files. If it’s a video though, we supply the onLongPress prop to Gifted Chat’s <Message> component. This allows us to listen for the event when a chat bubble is long-pressed. It’s what we use to render the fullscreen video:

    renderMessage = (msg) => {
      const { attachment } = msg.currentMessage;
      const renderBubble = (attachment && attachment.type.indexOf('audio') !== -1) ? this.renderPreview.bind(this, attachment.url) : null;
      const onLongPress = (attachment  && attachment.type.indexOf('video') !== -1) ? this.onLongPressMessageBubble.bind(this, attachment.url) : null;

      const modified_msg = {
        ...msg,
        renderBubble,
        onLongPress,
        videoProps: {
          paused: true // don't autoplay the video because it's just a preview
        }
      }
      return <Message {...modified_msg} />
    }

When a chat bubble that has a custom onLongPress method is long pressed, the app opens the modal for viewing the fullscreen video:

    onLongPressMessageBubble = (link) => {
      this.setState({
        is_video_modal_visible: true,
        video_uri: link // the direct link to the video
      });
    }

Next, here’s the code for rendering a custom chat bubble. This makes use of the <AudioPlayer> component which has already been pre-coded:

    renderPreview = (uri, bubbleProps) => {
      const text_color = (bubbleProps.position == 'right') ? '#FFF' : '#000';
      const modified_bubbleProps = {
        ...bubbleProps
      };
      return (
        <ChatBubble {...modified_bubbleProps}>
          <AudioPlayer url={uri} />
        </ChatBubble>
      );
    }

Typing indicators

To implement the typing indicators, we use Chatkit’s isTypingIn method to set the room where the current user is currently typing in. This will trigger the onUserStartedTyping hook on the other members of the room who are currently online:

    onTyping = async () => {
      try {
        await this.currentUser.isTypingIn({ roomId: this.room_id });
      } catch (typing_err) {
        console.log("error setting is typing: ", typing_err);
      }
    }

When the onUserStartedTyping hook is triggered, the following function is executed. This shows the chat UI’s footer text which displays the typing indicator:

    startTyping = (user) => {
      this.setState({
        is_typing: true,
        typing_user: user.name
      });
    }

If the user stops typing, the onUserStoppedTyping hook is automatically triggered so the following function is executed as well. This hides the chat UI’s footer text:

    stopTyping = (user) => {
      this.setState({
        is_typing: false,
        typing_user: null
      });
    }

Here’s the code for rendering the custom footer. All it does is show the name of the user who is currently typing in the chat room:

    renderFooter = () => {
      const { is_typing, typing_user } = this.state;
      if (is_typing) {
        return (
          <View style={styles.footerContainer}>
            <Text style={styles.footerText}>
              {typing_user} is typing...
            </Text>
          </View>
        );
      }
      return null;
    }

Read receipt

The last feature that we’re going to implement is the read receipt. This allows the current user to view the last message that another member of the room has read (or get all of the user’s read cursors). This makes use of Chatkit’s readCursor method to fetch the ID of the latest message that a specific user has marked as read (via the setReadCursor method):

    viewLastReadMessage = async (data) => {
      try {
        const cursor = await this.currentUser.readCursor({
          userId: data.userId,
          roomId: this.room_id
        });

        const viewed_message = this.state.messages.find(msg => msg._id == cursor.position);

        await this.setState({
          viewed_user: data.name,
          is_last_viewed_message_modal_visible: true,
          viewed_message: viewed_message.text
        });
      } catch (view_last_msg_err) {
        console.log("error viewing last message: ", view_last_msg_err);
      }
    }

Users can only view the message if it has already been loaded in their chat screen. If the message doesn’t show up then it means that the member they selected hasn’t read any of the recent messages that the current user is able to view.## Running the Chat app

At this point, you can now run the app. Start by running the server and exposing it to the internet using ngrok:

    cd server
    yarn start
    ~/Downloads/ngrok http 5000

Update the login and chat screen with your ngrok URL:

    // src/screens/Login.js, src/screens/Group.js, src/screens/Chat.js
    const CHAT_SERVER = "YOUR NGROK HTTPS URL";

Next, delete the node_modules/react-native-gifted-chat/node_modules/react-native-video folder. This is because we also have React Native Video as a dependency for the app. It’s conflicting with Gifted Chat’s copy, so we need to delete it.

Finally, run the app:

    react-native run-android
    react-native run-ios

Before you log a user in, be sure to visit <a href="http://localhost:5000/users" target="_blank">http://localhost:5000/users</a> on your browser to populate the users array in the server.

Conclusion

In this tutorial, you learned how to use various **Chatkit **features within a React Native app. Specifically, you learned how to implement roles and permissions, read receipts, file uploads, and typing indicators.

You can find the code in this GitHub repo.