cra-template-bod
Version:
The advanced TypeScript template for Bod CLI.
93 lines (85 loc) • 3.23 kB
text/typescript
import type { PayloadAction } from '@reduxjs/toolkit'
import type { AppThunk } from '../../store'
import { createAppSlice } from '../../utils/redux-utils'
import { fetchCount } from './service'
export interface CounterState {
value: number
status: 'idle' | 'loading' | 'failed'
}
const initialState: CounterState = {
value: 0,
status: 'idle',
}
// If you are not using async thunks you can use the standalone `createSlice`.
export const counterSlice = createAppSlice({
name: 'counter',
// `createSlice` will infer the state type from the `initialState` argument
initialState,
// The `reducers` field lets us define reducers and generate associated actions
reducers: create => ({
increment: create.reducer((state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1
}),
decrement: create.reducer((state) => {
state.value -= 1
}),
// Use the `PayloadAction` type to declare the contents of `action.payload`
incrementByAmount: create.reducer(
(state, action: PayloadAction<number>) => {
state.value += action.payload
},
),
// The function below is called a thunk and allows us to perform async logic. It
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
// will call the thunk with the `dispatch` function as the first argument. Async
// code can then be executed and other actions can be dispatched. Thunks are
// typically used to make async requests.
incrementAsync: create.asyncThunk(
async (amount: number) => {
const response = await fetchCount(amount)
// The value we return becomes the `fulfilled` action payload
return response.data
},
{
pending(state) {
state.status = 'loading'
},
fulfilled(state, action) {
state.status = 'idle'
state.value += action.payload
},
rejected(state) {
state.status = 'failed'
},
},
),
}),
// You can define your selectors here. These selectors receive the slice
// state as their first argument.
selectors: {
selectCount: counter => counter.value,
},
})
// Action creators are generated for each case reducer function.
export const { decrement, increment, incrementByAmount, incrementAsync }
= counterSlice.actions
// Selectors returned by `slice.selectors` take the root state as their first argument.
export const { selectCount } = counterSlice.selectors
/**
* We can also write thunks by hand, which may contain both sync and async logic.
* Here's an example of conditionally dispatching actions based on current state.
* @param {number} amount amount
* @returns {AppThunk} AppThunk
*/
export function incrementIfOdd(amount: number): AppThunk {
return (dispatch, getState) => {
const currentValue = selectCount(getState())
if (currentValue % 2 === 1 || currentValue % 2 === -1) {
dispatch(incrementByAmount(amount))
}
}
}