How to use Userbase in a React web app with TypeScript
Tags: userbase, typescript, react, tutorial
Published: 2020-01-30 03:10:13

Userbase is a new backend-as-a-service that promises a simple way to add user accounts and a general-purpose database to your web app. Here's how to set it up in a React web app with TypeScript.

This tutorial demonstrates the basic functionality of Userbase, including:

* User registration
* Login
* Logout
* Saving data

## Step 1: Setup

First, make sure you have Node ≥ 8.10 and npm ≥ 5.6 on your machine, and enter this command:

```
npx create-react-app userbase-react-typescript-demo --template typescript
```

This uses [create-react-app](https://github.com/facebook/create-react-app) to create a template project using TypeScript. Now install Userbase in your new project:

```
cd userbase-react-typescript-demo
npm install userbase-js
```

Next, go to [Userbase's web site](https://userbase.com) and create an admin account. A free account is limited to one app and three users, which is good enough for our purposes. The web site will give you an app ID. Copy this, create a new file named `.env` in the root of your project, and paste your app ID like so:

```
REACT_APP_USERBASE_APP_ID=5e363c2a-7a29-47c8-9496-bd26cc5637ee
```

With the boring setup stuff out of the way, we can start coding.

## Step 2: Add User Registration

(If you want to skip ahead to the completed app, check it out [here](https://github.com/danbockapps/userbase-react-typescript-demo/blob/master/src/App.tsx).)

Open up App.tsx and delete the contents of the file. Replace it with the following. First, import some things we'll need from React and Userbase:

```typescript
import React, { ChangeEvent, FormEvent, useEffect, useState } from 'react'
import userbase, { UserResult } from 'userbase-js'
```

Next, the start of the component and a `user` state variable to keep track of whether the user is logged in. When `user` is undefined, that will mean the user is logged out. When it's defined, that will mean the user is logged in.

```typescript
const App: React.FC = () => {
  const [user, setUser] = useState<UserResult>()
```

Next is the code we need to run to initialize Userbase when the user first opens the page. This picks up the app ID you saved in the `.env` file earlier.

```typescript
useEffect(() => {
  userbase
  .init({ appId: process.env.REACT_APP_USERBASE_APP_ID as string })
  .then(session => session.user && setUser(session.user))
}, [])
```

Now a couple of things to manage the form that the user will type into:

```typescript
const [regForm, setRegForm] = useState<{
  username?: string
  password?: string
}>({ username: '', password: '' })
const handleRegInputChange = (event: ChangeEvent<HTMLInputElement>) =>
  setRegForm({ ...regForm, [event.target.name]: event.target.value })
```

The last function is the one to send the user data to Userbase when the user registers:

```typescript
const handleRegSubmit = (event: FormEvent<HTMLFormElement>) => {
  event.preventDefault()
  if (regForm.username && regForm.password)
    userbase
      .signUp({
        username: regForm.username,
        password: regForm.password,
        rememberMe: 'local'
      })
      .then((ur: UserResult) => setUser(ur))
      .catch(err => alert(err))
}
```

Finally, return the JSX containing the form, and export the component:

```typescript
  return (
    <div>
      <h2>Register</h2>
      <form onSubmit={handleRegSubmit}>
        <label>
          Username:
          <input
            type="text"
            name="username"
            value={regForm?.username}
            onChange={handleRegInputChange}
          />
        </label>

        <label>
          Password:
          <input
            type="password"
            name="password"
            value={regForm?.password}
            onChange={handleRegInputChange}
          />
        </label>
        <input type="submit" value="Submit" />
      </form>
    </div>
  )
}

export default App
```

[Here's the full App.tsx after step 2.](https://github.com/danbockapps/userbase-react-typescript-demo/blob/9848b8803e42ea05401f50414c6b4df3150f6bfb/src/App.tsx)

## Step 3: Add Login

The login code is going to look a lot like the registration code. The reason I'm not refactoring it here into a more [DRY](https://www.codementor.io/@joshuaaroke/dry-code-vs-wet-code-89xjwv11w) style is that in the real world you'd probably want to add different styling, input validation and other things to the login code than to the registration code.

The functions:

```typescript
  const [loginForm, setLoginForm] = useState<{
    username?: string
    password?: string
  }>({ username: '', password: '' })

  const handleLoginInputChange = (event: ChangeEvent<HTMLInputElement>) =>
    setLoginForm({ ...loginForm, [event.target.name]: event.target.value })

  const handleLoginSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault()
    if (loginForm.username && loginForm.password)
      userbase
        .signIn({
          username: loginForm.username,
          password: loginForm.password,
          rememberMe: 'local'
        })
        .then((ur: UserResult) => setUser(ur))
        .catch(err => alert(err))
  }
```

And the JSX:

```html
          <h2>Log in</h2>
          <form onSubmit={handleLoginSubmit}>
            <label>
              Username:
              <input
                type="text"
                name="username"
                value={loginForm?.username}
                onChange={handleLoginInputChange}
              />
            </label>

            <label>
              Password:
              <input
                type="password"
                name="password"
                value={loginForm?.password}
                onChange={handleLoginInputChange}
              />
            </label>
            <input type="submit" value="Submit" />
          </form>
```

[Here's the full App.tsx after step 3.](https://github.com/danbockapps/userbase-react-typescript-demo/blob/62fb3d818f8feabb549a82cba64bee37b9ed4f04/src/App.tsx)

## Step 4: Add Logout

The function to handle logout is pretty simple. It updates our app's state by calling `setUser(undefined)` when it gets the signal from Userbase that the user has been logged out.

```typescript
  const handleLogout = () => {
    userbase
      .signOut()
      .then(() => setUser(undefined))
      .catch(err => alert(err))
  }
```

In the return statement, we need to add a new conditional. If the `user` state variable is defined, that means the user is signed in, and the app needs to display "Signed in as" and the user's name, plus a button the user can click on to log out. If `user` is undefined, that means the user is not signed in, and the registration and login options should be shown.

The return statement should now look like this:

```typescript
  return (
    <div>
      {user ? (
        <div>
          <div>
            Signed in as {user.username}.{' '}
            <button onClick={handleLogout}>Log out</button>
          </div>
        </div>
      ) : (
        <div>
          <h2>Register</h2>
          <form onSubmit={handleRegSubmit}>
            <label>
              Username:
              <input
                type="text"
                name="username"
                value={regForm?.username}
                onChange={handleRegInputChange}
              />
            </label>

            <label>
              Password:
              <input
                type="password"
                name="password"
                value={regForm?.password}
                onChange={handleRegInputChange}
              />
            </label>
            <input type="submit" value="Submit" />
          </form>

          <h2>Log in</h2>
          <form onSubmit={handleLoginSubmit}>
            <label>
              Username:
              <input
                type="text"
                name="username"
                value={loginForm?.username}
                onChange={handleLoginInputChange}
              />
            </label>

            <label>
              Password:
              <input
                type="password"
                name="password"
                value={loginForm?.password}
                onChange={handleLoginInputChange}
              />
            </label>
            <input type="submit" value="Submit" />
          </form>
        </div>
      )}
    </div>
  )
```

[Here's the full App.tsx after step 4.](https://github.com/danbockapps/userbase-react-typescript-demo/blob/61b4d4460203e8c9b33e14c8a20efdbca8727b86/src/App.tsx)

## Step 5: Save data

We need to add the `Item` import:

```typescript
import userbase, { Item, UserResult } from 'userbase-js'
```

Inside the component, whenever `user` goes from undefined to defined, we need to call the `openDatabase` function. Our database will be called `blueButton`. Notice the `changeHandler` function that gets passed to Userbase here. That'll get defined below.

```typescript
  const DATABASE_NAME = 'blueButton'

  useEffect(() => {
    if (user)
      userbase.openDatabase({ databaseName: DATABASE_NAME, changeHandler })
  }, [user])
```

We're going to have a button the user clicks, and the timestamp of each click will be saved to Userbase. The app will display the number of clicks on the screen. To do that, we need a state variable for number of clicks:

```typescript
  const [numClicks, setNumCicks] = useState<number>()
```

And a function to insert into the database. This inserts a new record, with a JavaScript `Date` object for the current date and time, into the database. It will get called when the user clicks the button.

```typescript
  const handleBlueButtonClick = () => {
    userbase.insertItem({ databaseName: DATABASE_NAME, item: new Date() })
  }
```

Here's one of the neatest parts of Userbase: the `changeHandler` function. We passed this function in as an argument to `userbase.openDatabase` above. As you can see, this function sets our `numClicks` state variable. But when does it get called? It gets called whenever the data in the database changes - _even if it was changed from another device_. There's a video of this neat trick below.

```typescript
  const changeHandler = (items: Item[]) => {
    setNumCicks(items.length)
  }
```

Finally, here's the JSX that displays our big blue button and the number of times it's been clicked.

```typescript
          <div>
            <h2>Click the blue button</h2>
            <button
              style={{
                fontSize: '25px',
                backgroundColor: 'blue',
                color: 'white'
              }}
              onClick={handleBlueButtonClick}
            >
              The Blue Button
            </button>
            <div style={{ marginTop: '25px' }}>
              You have clicked: {numClicks} times.
            </div>
          </div>
```

Here's the video of the final product in action. Userbase is using [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) behind the scenes, but you don't need to know anything about how those work.

{% youtube Ub4dQOTIc-Y %}

[Here's the final version of App.tsx.](https://github.com/danbockapps/userbase-react-typescript-demo/blob/a643a6a200ae19ef51d5bbaa50ec34bcf2dae05d/src/App.tsx)