Using React Hooks

I’m writing code with React and TypeScript lately and using React Hooks in the app.

This new APIs makes the React app development very easier and I love it!

In this post, I will write about what I learned and a kind of tips to use them.

If you wanna learn more precise details, just read official docs.

Dependencies

  • typescript v3.4.5
  • react v16.8.6
  • react-router-dom v5.0.0

useState

This function is very easy. But just be careful when using null value for the initial state.

Like following examples, I set the object state as nullable, but it is not recognized as I expected in TypeScript(TS) environment.

You use blank object in that case.

import React, { useState } from 'react'

interface User {
  id: number
  name: string
}

const App = () => {
  const initialState: User | null = null
  // This DOESN'T work
  const [user, setUser] = useState(null)

  return (
    <>
      <h2>Hello!</h2>
      <p>{user ? user.name : ''}</p>
      <button onClick={() => setUser({ id: 1, name: 'mmyoji' }) }></button>
    </>
  )
}


// You should do like following instead

interface User {
  id?: number // or passing default key-value for this attribute
  name?: string
}

const App = () => {
  const [user, setUser] = useState({})
  // ...
}

useEffect

This is a replacement of componentDidXXX and componentWillXXX APIs.

Here’s the details: https://reactjs.org/docs/hooks-effect.html

When you use async function inside useEffect, you can write like this:

import React, { useEffect } from "react"

const App = () => {
  useEffect(() => {
    (async () => {
      // fetching User object through API request
      const user = await fetchUser()
      // ...
    })()
  })

  return <></>
}

Custom Hooks

Sometimes (or often) you wanna use the same useState and useEffect set in multiple components.

In that case, you create Custom Hooks as a simple function.

Details: https://reactjs.org/docs/hooks-custom.html

import { useEffect, useState } from "react"

type User {
  id: number
  name: string
}

export function useAuthUser(): User | null {
  const initialUser: { user: User |  null } = { user: null }
  const [user, setUser] = useState(initialUser)
  useEffect(() => {
    subscribeAuthState((user) => {
      if (user) {
        setUser({ user })
      } else {
        setUser({ user: null })
      }
    })
   })

  return user.user
}


// In other file,
import { useAuthUser } from "./auth"

const App = () => {
    const user = useAuthUser()
    // ...
}

This example is not so good because it is better to use useContext for managing authenticated user of a web app, I guess.

useContext

This is a Hooks type API of Context.Consumer of React.createContext

Read this https://reactjs.org/docs/context.html if you don’t know React’s Context API.

This is for managing global state of an app like authenticated user, color theme, etc.

This is a bit complicated to use compared with other APIs.

import React, { createContext, ReactNode } from 'react'

interface User {
  id: number
  name: string
}

const initialContext: { user: User | null } = { user: null }

// The argument of createContext must be the same type for Provider's `value`.
export const AuthContext = createContext(initialContext)

interface ChildrenProps {
  children: ReactNode
}

export default AuthProvider = ({ children }: ChildrenProps) => {
  // You can write useState or useEffect for this context.

  return (
    <AuthContext.Provider value={{ user: user }}>{children}</AuthContext.Provider>
  )
}


// Wrap your Parent componet with the Provider component

const App = () => {
  return (
    <AuthProvider>
      <AComponent/>
      <BComponent/>
    </AuthProvider>
  )
}

Just be careful when you use react-router-dom because I was in trouble in the case.

In short, you should wrap BrowserRouter w/ the Provider component if you use useContext in some components which are defined as Route component.

Finally

I personally think that redux is too much and a bit difficult.

React Hooks solves such kind of problems in smarter way.

And I can’t write pure JavaScript because of TypeScript experience :)

Contents