Skip to content

Instantly share code, notes, and snippets.

@yuki-yano
Last active January 20, 2021 08:36
Show Gist options
  • Select an option

  • Save yuki-yano/fcc2520d6e02fb923ced7189188bce3d to your computer and use it in GitHub Desktop.

Select an option

Save yuki-yano/fcc2520d6e02fb923ced7189188bce3d to your computer and use it in GitHub Desktop.
typescript-redux-thunk-sample
import axios from "axios"
import { Reducer, Dispatch } from "redux"
import Cookies from "js-cookie"
export type UserState = {
accessToken: string
loggingIn: boolean
error: string
}
const initialState: UserState = {
accessToken: "",
loggingIn: false,
error: ""
}
type Action = SigninStartedAction | ReturnType<typeof signinDone | typeof signinFailed>
export type SigninStartedAction = {
type: typeof SIGNIN_STARTED
payload: SigninStartedPayload
}
export type SigninStartedPayload = {
code: string
state: string
}
export type SigninDonePayload = {
accessToken: string
}
export type SigninFailedPayload = {
error: string
}
const SIGNIN_STARTED = "SIGNIN_STARTED"
const SIGNIN_DONE = "SIGNIN_DONE"
const SIGNIN_FAILED = "SIGNIN_FAILED"
export const signinStarted = (payload: SigninStartedPayload) => async (dispatch: Dispatch) => {
if (Cookies.get("SigninState") !== payload.state) {
return dispatch(actions.signinFailed({ error: "Error invalid state" }))
}
const res = await axios.post("//localhost:3000/auth/slack", { code: payload.code })
if (res.status === 200) {
return dispatch(actions.signinDone({ accessToken: res.data.access_token }))
} else {
return dispatch(actions.signinFailed({ error: `Error ${res.status}: ${res.statusText}` }))
}
}
const signinDone = (payload: SigninDonePayload) => ({
type: SIGNIN_DONE as typeof SIGNIN_DONE,
payload
})
const signinFailed = (payload: SigninFailedPayload) => ({
type: SIGNIN_FAILED as typeof SIGNIN_FAILED,
payload
})
export const actions = { signinStarted, signinDone, signinFailed }
export const userReducer: Reducer<UserState, Action> = (state = initialState, action) => {
switch (action.type) {
case SIGNIN_STARTED: {
return {
...state,
loggingIn: true
}
}
case SIGNIN_DONE: {
return {
...state,
accessToken: action.payload.accessToken,
loggingIn: false
}
}
default: {
const _: never = action
return state
}
}
}
import React from "react"
import { Action } from "redux"
import { ThunkDispatch } from "redux-thunk"
import { connect } from "react-redux"
import { signinStarted, SigninStartedPayload, UserState } from "../modules/User"
import { AppState } from "../store"
type Props = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>
const CallBack: React.FC<Props> = (props: Props) => {
const url = new URL(document.URL)
const state = url.searchParams.get("state") || ""
const code = url.searchParams.get("code") || ""
return (
<div>
<div>Login Processing</div>
<a onClick={() => props.onSignin({ code, state })}>hoge</a>
</div>
)
}
const mapStateToProps = (state: AppState) => ({
...state.user
})
const mapDispatchToProps = (dispatch: ThunkDispatch<UserState, void, Action>) => ({
onSignin(payload: SigninStartedPayload) {
dispatch(signinStarted(payload))
}
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(CallBack)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment