React Hooks
by mmyoji
3 min read
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 :)