Table of Contents:
- Initializing State
- createStore Pattern
- Reducer Pattern
- Closing Thoughts
1. Initializing State
In Redux, all your application state is held in the store; which is an object that holds the complete state tree of your app. There is only one way to change its state and that is by dispatching actions.
Actions are objects that consist of a type and a payload property. They are created and dispatched by special functions called action creators.
see a small contrived and detailed example below:
First creating the Redux store:
import { createStore } from 'redux'
function todosReducer(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.payload])
default:
return state
}
}
const store = createStore(todosReducer)
Next updating the store
const ADD_TODO = add_todo; // creates the action type
const newTodo = ["blog on dev.to"];
function todoActionCreator (newTodo) {
const action = {
type: ADD_TODO,
payload: newTodo
}
dispatch(action)
}
Like I said my examples are small and contrived and are aimed at clearing the air, around the aspect of Redux related to our discussion. So, kindly see them as a refresher. I am assuming you already have some knowledge of Redux and are familiar with some patterns in a React Redux environment.
However regardless of your professional experience, I would not be surprised if you find a hoe on these premises because too many people know too much but actually know nothing at all. Like in my old article on hoisting in JavaScript, a lot of developers where making thunderclaps around the web about hoisting, but they were all explaining the concept wrongly.
So my advice is kindly to take it and weed around your own premises. If you do I am sure you would come out with a better understanding of React and Redux.
When a store is created, Redux dispatches a dummy action to your reducer to populate the store with the initial state. You are not meant to handle the dummy action directly. Just remember that your reducer should return some kind of initial state if the state given to it as the first argument is undefined.
But you don't want your initial application state to be undefined, so you have to initialize the state yourself. There are two ways or patterns to do this viz: the createStore pattern and the reducers pattern.
2. createStore Pattern
The createStore method can accept an optional preloadedState value as its second argument. In our example, we called createStore without passing this value. When a value is passed to the preloadedState it becomes the initial state.
const initialState = [] // in a real-world, is way better to initialize state with {} (an object) instead of an array.
const store = createStore(todosReducer, initialState)
let's say we have a compulsory todo list for everyone and we can add new tasks to this list later. In this case, we would initialize state like this:
const initialState = ["eat", "code", "sleep"]; // compulsory todo list
const store = createStore(todosReducer, initialState)
3. Reducer Pattern
Reducers can also specify an initial state value by looking for an incoming state argument that is undefined, and returning the value they'd like to use as a default. In our example above our todoReducer already does this.
function todosReducer(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.payload])
default:
return state
}
}
// sets initial state to []. But would only take effect if the initial state is undefined, which means it was not set using createStore().
But there is a drawback to this method. In our contrived example, it can be great but what of in a large application where we have about 10 or 20 reducers and we want to initialize state not just with an empty array or object literal, but with some data. It would take a lot of repetition to get this done with the reducers and it is going to be tedious if we decide to change that initial state data at some point.
That's a really boring thing todo:
4. Closing Thoughts:
You might be wondering which method is best to use. I have already pointed out a drawback with the reducer pattern.
In general, preloadedState wins over the state specified by the reducer. This lets reducers specify initial data that makes sense to them as default arguments, but also allows loading existing data (fully or partially) when you're hydrating the store from some persistent storage or the server.
Also Note:
Reducers whose initial state is populated using preloadedState will still need to provide a default value to handle when passed a state of undefined. All reducers are passed undefined on initialization, so they should be written such that when given undefined, some value should be returned. This can be any non-undefined value; there's no need to duplicate the section of preloadedState here as the default
I have seen developers use both methods but as touching the drawback I noted about using reducers, I would love to hear your opinions and experiences in the comment section below.
Top comments (0)