Contextなauthでもstateをtype alias使ってunionにすると便利
Stateを複数つけてunion typeにすることで、stateの状態決定できるの便利では説
code:typescript
// これだと推論が不完全でConsumer側でcurrentUserも確認しなきゃいけない
// type ContextState = {
// isLoginCheckCompleted: boolean
// isLoggedIn?: boolean // 冗長?
// currentUser?: AppUser
// }
type AuthInitialState = {
isLoginCheckCompleted: false
}
type AuthLoginState = {
isLoginCheckCompleted: true
isLoggedIn: true
currentUser: AppUser
}
type AuthNotLoginState = {
isLoginCheckCompleted: true
isLoggedIn: false
currentUser: undefined
}
type ContextState = AuthInitialState | AuthLoginState | AuthNotLoginState
const initilState: ContextState = {
isLoginCheckCompleted: false
}
const AuthContext = React.createContext<ContextState>(initilState)
code:typescript
export const Header = () => {
return (
<AuthConsumer>
{(props) => {
if (!props.isLoginCheckCompleted || !props.isLoggedIn) {
return null // empty
}
// props.currentUserが推論されきっている
return <div>{props.currentUser.displayName}</div>
}}
</AuthConsumer>
)
}
と思ったけどAuthStateのそれぞれはinterfaceにしたほう良くない?
code:typescript
interface AuthState {
status: string
}
interface AuthInitialState extends AuthState {
status: "Initial"
}
interface AuthLoginState extends AuthState {
status: "Login"
isLoggedIn: true
currentUser: AppUser
}
interface AuthNotLoginState extends AuthState {
status: "NotLogin"
isLoggedIn: false
currentUser: undefined
}
type ContextState = AuthInitialState | AuthLoginState | AuthNotLoginState
確かに。これだと statusの名前を書いたとき一括で補完される。type aliasだとこうはならない。
最終型
code:typescript
import React, { Context } from "react"
import { auth } from "~/firebase-app/auth"
import { AppUser, getUserFromFirebaseUser } from "~/model/user"
interface AuthState {
status: string
}
interface AuthInitialState extends AuthState {
status: "Initial"
}
interface AuthLoginState extends AuthState {
status: "Login"
currentUser: AppUser
}
interface AuthNotLoginState extends AuthState {
status: "NotLogin"
// currentUser?: null | undefined
}
type ContextState = AuthInitialState | AuthLoginState | AuthNotLoginState
const initilState: ContextState = {
status: "Initial"
}
const AuthContext = React.createContext<ContextState>(initilState)
export class AuthContainer extends React.Component<{}, ContextState> {
setUser(user: AppUser | null) {
const status = !!user ? "Login" : "NotLogin"
switch (status) {
case "Login":
return this.setState({ status, currentUser: user })
default:
return this.setState({ status })
}
}
componentWillMount() {
auth.onAuthStateChanged((user) => {
if (user == null) {
return this.setUser(user)
}
const appUser = getUserFromFirebaseUser(user)
return this.setUser(appUser)
})
}
render() {
return (
<AuthContext.Provider value={this.state}>
{this.props.children}
</AuthContext.Provider>
)
}
}
export const AuthConsumer = AuthContext.Consumer