Redux Toolkit has been around for a while now. It's still the same state manager we love with a kinda new pattern but still familiar and less overhead a.k.a it's "baggage"!
Since it's 2021, RTL and typescript are perfect together. It would be a crime not to use it together.
We Begin!
Start with installing Redux and RTL to React.
npm install @reduxjs/toolkit react-redux
The Pattern
As it always is, we start with our store
file.
Create the file inside app
folder, which path will look like src/app/store.ts
. Then lets import configureStore
import { configureStore } from '@reduxjs/toolkit'
// export the store so we can pass it to our Provider wrapper
export const store = configureStore({
reducer: {},
})
// Notice that we are exporting inferred typing to be used later for some cool type hintings
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
Now, don't forget to wrap your whole app with Redux Provider
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { store } from './app/store'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Hooks
Before we move on, remember the types we exported from the store earlier? We are going to use that to type charge the hooks for dispatch
and selector
src/app/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import { AppDispatch, RootState } from './store'
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
With these you'll get typing on what ever you put into your reducer and initial state when using the Redux hooks
Slice
Slice is just where your reducer and action is going to be if your still thinking of classic Redux pattern.
The recommended file structure should follow this file structure pattern features/counter/counterSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
export interface CounterState {
value: number
}
const initialState: CounterState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (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: (state) => {
state.value -= 1
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload
},
},
})
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
Then we add the slice to our store
app/store.ts
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export default configureStore({
reducer: {
counter: counterReducer,
},
})
Usage
import React from 'react'
import { useAppSelector, useAppDispatch } from '../../app/hooks'
import { decrement, increment } from './counterSlice'
export function Counter() {
const count = useAppSelector((state) => state.counter.value)
const dispatch = useAppDispatch()
return (
<div>
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
)
}
Top comments (0)