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 :)