The plan:
You can not make API calls and routing transitions inside a
reducer
This! This basically means you are bound to use some third party library to handle updating state with data returned by the API calls, hello middleware
! You must use something like redux-thunk
or redux-saga
, there is no two ways about it. Because:
{}
{}
function ()
1npm i redux react-redux @types/react-redux
This is all you need to do in order to get started
store
reducer
<Provider>
component to be able to access the store from anywhere in the app 1import React from 'react'
2import ReactDOM from 'react-dom'
3import App from './App'
4import { createStore } from 'redux'
5import { Provider } from 'react-redux'
6
7const rootReducer = (state = {}, action: {}) => {
8 return state
9}
10
11const store = createStore(rootReducer)
12
13ReactDOM.render(
14 <Provider store={store}>
15 <App />
16 </Provider>,
17 document.getElementById('root')
18)
You need to do the following setup once in order to use Redux inside your react app
store
We need createStore()
from redux
in order to cerate a store.
1import { createStore } from 'redux`
2
3const store = createStore(reducer, preLoadedState) // optional third arg is middleware (say sagas or thunk)
reducer
. We need at least one to get started(previousState, action) => newState
The reducer is a pure function that takes the previous state and an action, and returns the next state.
1const reducer = (state = {}, action: {}) => {
2 return state
3}
Then we’ll use the <Provider>
component and wrap our entire app inside it. This will make the entire app (i.e. wherever we want to use values from the store) be able to access the store. <Provider>
will take a store
property.
1ReactDOM.render(
2 <Provider store={store}>
3 <App />
4 </Provider>,
5 document.getElementById('root')
6)
Actions are what tell you what to change in state. The reducer
will then change the state based on whatever action was given. You can either write an if
statement or a switch
. Convention is to use switch cases.
1const rootReducer = (state = {}, action: {}) => {
2 return state
3}
will become
1const rootReducer = (state = initialState, action: { type: string }) => {
2 switch (action.type) {
3 case 'INCREMENT':
4 // do something
5 return { count: state.count + 1 }
6 case 'DECREMENT':
7 // do something
8 return { count: state.count - 1 }
9 default:
10 return state
11 }
12}
Action is just an object. type
is by convention, you can call it whatever, and it doesn’t necessarily have to be a string, but that’s also convention.
Now comes the fun part, and the part that Redux has improved on, managing the state using hooks
useDispatch()
- dispatch an actionuseSelector()
- selects relevant parts of the state object, takes a callback1const count = useSelector((state: any) => state.counter) // this will give you just the `count` property from the state
2const dispatch = useDispatch() // this will let you send actions
3
4dispatch({ type: 'DECREMENT' })
Action is an object. So whenever you dispatch actions, you’d have to give dispatch()
an action object (with the type that we are expecting)