React Redux for Beginners(part_7)

part1 & part2 & part3 & part4 & part5 & part6

Sanje Qi
4 min readOct 27, 2019

Reducers

Reducers specify how the application’s state changes in response to actions sent to the store. Remember that actions only describe what happened, but don’t describe how the application’s state changes.

Designing the State Shape

In Redux, all the application state is stored as a single object. It’s a good idea to think of its shape before writing any code. What’s the minimal representation of your app’s state as an object?

For our todo app, we want to store two different things:

  • The currently selected visibility filter.
  • The actual list of todos.

You’ll often find that you need to store some data, as well as some UI state, in the state tree. This is fine, but try to keep the data separate from the UI state.

{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}

Handling Actions

Now that we’ve decided what our state object looks like, we’re ready to write a reducer for it. The reducer is a pure function that takes the previous state and an action, and returns the next state.

(previousState, action) => newStateCopy

It’s called a reducer because it’s the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue). It's very important that the reducer stays pure. Things you should never do inside a reducer:

  • Mutate its arguments;
  • Perform side effects like API calls and routing transitions;
  • Call non-pure functions, e.g. Date.now() or Math.random().

We’ll explore how to perform side effects in the advanced walkthrough. For now, just remember that the reducer must be pure. Given the same arguments, it should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Just a calculation.

With this out of the way, let’s start writing our reducer by gradually teaching it to understand the actions we defined earlier.

We’ll start by specifying the initial state. Redux will call our reducer with an undefined state for the first time. This is our chance to return the initial state of our app:

import { VisibilityFilters } from './actions'const initialState = {
visibilityFilter: VisibilityFilters.SHOW_ALL,
todos: []
}
function todoApp(state, action) {
if (typeof state === 'undefined') {
return initialState
}
// For now, don't handle any actions
// and just return the state given to us.
return state
}

Now let’s go back to our example in part 6

REDUX STORE + REDUCERS

This section will finally introduce Redux to manage the state of the stories instead of passing them directly into to your App component from your React entry point. Let’s approach it step by step. First, you have to install Redux on the command line:

npm install --save redux

Second, in the React entry point file, you can import the Redux store which is not defined in the other file yet. We will do this in a moment. Now, instead of using the sample stories, you will use the stories that are stored in the Redux store. Taken that the store only saves a list of stories as state, you can simply get all the global state of the store and assume that it is the list of stories.

import React from 'react';import ReactDOM from 'react-dom';import App from './components/App';import store from './store';import './index.css';ReactDOM.render(<App stories={store.getState()} onArchive={() => {}} />,document.getElementById('root'));

Third, you have to create your Redux store instance in a separate src/store/index.js file. It already takes a reducer that is not implemented yet, but which you will implement in a moment.

import { createStore } from 'redux';import storyReducer from '../reducers/story';const store = createStore(storyReducer);export default store;

Fourth, in your src/reducers/ folder you can create your first reducer called storyReducer in a src/reducers/story.js file. It doesn't react to any actions yet.

function storyReducer(state, action) {switch(action.type) {default : return state;}}export default storyReducer;

Also it can have the sample stories as initial state. You have learned before how initial state in Redux can be initialized when creating the Redux store. This is another way of creating initial state on a reducer level:

const INITIAL_STATE = [{title: 'React',url: 'https://facebook.github.io/react/',author: 'Jordan Walke',num_comments: 3,points: 4,objectID: 0,}, {title: 'Redux',url: 'https://github.com/reactjs/redux',author: 'Dan Abramov, Andrew Clark',num_comments: 2,points: 5,objectID: 1,},];function storyReducer(state = INITIAL_STATE, action) {switch(action.type) {default : return state;}}export default storyReducer;

Your application should work when you start it. It is using the state from the Redux store that is initialized in the storyReducer, because it is the only reducer in your application. There are no actions yet and no action is captured in the reducer yet. Even though there was no action dispatched, you can see that the Redux store runs once through all its defined reducers to initialize its initial state from the reducers in the store. The state gets visible through the Stories and Story components, because it is passed down from the React entry point file.

Source: StackOverflow, Robin Wieruch, Medium, ReactJs, MDN, bitsrc

--

--