# refactoring-reducer-example

[**Redux**](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/index.html)

[Getting Started](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/getting-started.html)[Tutorial](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/essentials/part-1-overview-concepts.html)[API](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/api/api-reference.html)[FAQ](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq.html)[Best Practices](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/style-guide/style-guide.html)[GitHub](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/github.com/reduxjs/redux.html)[Need help?](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/getting-started.html#help-and-discussion)

🌜

🌞

Search

[**Redux**](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/index.html)

* [Getting Started](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/getting-started.html)
* [Tutorial](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/essentials/part-1-overview-concepts.html)
* [API](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/api/api-reference.html)
* [FAQ](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq.html)
* [Best Practices](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/style-guide/style-guide.html)
* [GitHub](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/github.com/reduxjs/redux.html)
* [Need help?](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/getting-started.html#help-and-discussion)
* [Introduction](#!)
  * [Getting Started with Redux](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/getting-started.html)
  * [Installation](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/installation.html)
  * [Core Concepts](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/core-concepts.html)
  * [Learning Resources](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/learning-resources.html)
  * [Ecosystem](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/ecosystem.html)
  * [Examples](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/examples.html)
* [Tutorials](#!)
  * [Tutorials Index](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/index.html)
  * [Quick Start](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/quick-start.html)
  * [TypeScript Quick Start](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/typescript-quick-start.html)
  * [Redux Essentials](#!)
    * [Redux Overview and Concepts](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/essentials/part-1-overview-concepts.html)
    * [Redux App Structure](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/essentials/part-2-app-structure.html)
    * [Basic Redux Data Flow](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/essentials/part-3-data-flow.html)
    * [Using Redux Data](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/essentials/part-4-using-data.html)
    * [Async Logic and Data Fetching](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/essentials/part-5-async-logic.html)
    * [Performance and Normalizing Data](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/essentials/part-6-performance-normalization.html)
  * [Redux Fundamentals](#!)
    * [Redux Overview](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/fundamentals/part-1-overview.html)
    * [Redux Concepts and Data Flow](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/fundamentals/part-2-concepts-data-flow.html)
    * [State, Actions, and Reducers](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/fundamentals/part-3-state-actions-reducers.html)
    * [Store](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/fundamentals/part-4-store.html)
    * [UI and React](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/fundamentals/part-5-ui-react.html)
    * [Async Logic and Data Fetching](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/fundamentals/part-6-async-logic.html)
    * [Standard Redux Patterns](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/fundamentals/part-7-standard-patterns.html)
    * [Modern Redux with Redux Toolkit](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/fundamentals/part-8-modern-redux.html)
* [Recipes](#!)
  * [Recipes: Index](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/recipe-index.html)
  * [Configuring Your Store](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/configuring-your-store.html)
  * [Usage With TypeScript](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/usage-with-typescript.html)
  * [Migrating to Redux](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/migrating-to-redux.html)
  * [Using Object Spread Operator](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/using-object-spread-operator.html)
  * [Reducing Boilerplate](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/reducing-boilerplate.html)
  * [Server Rendering](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/server-rendering.html)
  * [Writing Tests](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/writing-tests.html)
  * [Computing Derived Data](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/computing-derived-data.html)
  * [Implementing Undo History](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/implementing-undo-history.html)
  * [Isolating Redux Sub-Apps](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/isolating-redux-sub-apps.html)
  * [Code Splitting](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/code-splitting.html)
  * [Troubleshooting](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/troubleshooting.html)
  * [Structuring Reducers](#!)
    * [Structuring Reducers](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/structuring-reducers.html)
    * [Prerequisite Concepts](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/prerequisite-concepts.html)
    * [Basic Reducer Structure](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/basic-reducer-structure.html)
    * [Splitting Reducer Logic](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/splitting-reducer-logic.html)
    * [Refactoring Reducers Example](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/refactoring-reducer-example.html)
    * [Using combineReducers](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/using-combinereducers.html)
    * [Beyond combineReducers](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/beyond-combinereducers.html)
    * [Normalizing State Shape](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/normalizing-state-shape.html)
    * [Updating Normalized Data](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/updating-normalized-data.html)
    * [Reusing Reducer Logic](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/reusing-reducer-logic.html)
    * [Immutable Update Patterns](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/immutable-update-patterns.html)
    * [Initializing State](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/md-docs/initializing-state.html)
* [Understanding Redux](#!)
  * [Thinking in Redux](#!)
    * [Motivation](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/understanding/thinking-in-redux/motivation.html)
    * [Three Principles](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/understanding/thinking-in-redux/three-principles.html)
    * [Glossary](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/understanding/thinking-in-redux/glossary.html)
  * [History and Design](#!)
    * [Prior Art](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/understanding/history-and-design/prior-art.html)
    * [Middleware](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/understanding/history-and-design/middleware.html)
* [FAQ](#!)
  * [FAQ Index](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq.html)
  * [General](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/general.html)
  * [Reducers](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/reducers.html)
  * [Organizing State](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/organizing-state.html)
  * [Store Setup](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/store-setup.html)
  * [Actions](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/actions.html)
  * [Immutable Data](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/immutable-data.html)
  * [Code Structure](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/code-structure.html)
  * [Performance](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/performance.html)
  * [Design Decisions](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/design-decisions.html)
  * [React Redux](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/react-redux.html)
  * [Miscellaneous](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq/miscellaneous.html)
* [Style Guide](#!)
  * [Style Guide: Best Practices](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/style-guide/style-guide.html)
* [API Reference](#!)
  * [API Reference](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/api/api-reference.html)
  * [createStore](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/api/createstore.html)
  * [Store](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/api/store.html)
  * [combineReducers](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/api/combinereducers.html)
  * [applyMiddleware](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/api/applymiddleware.html)
  * [bindActionCreators](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/api/bindactioncreators.html)
  * [compose](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/api/compose.html)
* [Redux Toolkit](#!)
  * [Redux Toolkit: Overview](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/redux-toolkit/overview.html)

## Refactoring Reducer Logic Using Functional Decomposition and Reducer Composition[#](#refactoring-reducer-logic-using-functional-decomposition-and-reducer-composition)

It may be helpful to see examples of what the different types of sub-reducer functions look like and how they fit together. Let's look at a demonstration of how a large single reducer function can be refactored into a composition of several smaller functions.

> **Note**: this example is deliberately written in a verbose style in order to illustrate the concepts and the process of refactoring, rather than perfectly concise code.

**Initial Reducer**[**#**](#initial-reducer)

Let's say that our initial reducer looks like this:

const initialState = {

visibilityFilter: 'SHOW\_ALL',

todos: \[]

}

function appReducer(state = initialState, action) {

switch (action.type) {

case 'SET\_VISIBILITY\_FILTER': {

return Object.assign({}, state, {

visibilityFilter: action.filter

})

}

case 'ADD\_TODO': {

return Object.assign({}, state, {

todos: state.todos.concat({

id: action.id,

text: action.text,

completed: false

})

})

}

case 'TOGGLE\_TODO': {

return Object.assign({}, state, {

todos: state.todos.map(todo => {

if (todo.id !== action.id) {

return todo

}

return Object.assign({}, todo, {

completed: !todo.completed

})

})

})

}

case 'EDIT\_TODO': {

return Object.assign({}, state, {

todos: state.todos.map(todo => {

if (todo.id !== action.id) {

return todo

}

return Object.assign({}, todo, {

text: action.text

})

})

})

}

default:

return state

}

}

Copy

That function is fairly short, but already becoming overly complex. We're dealing with two different areas of concern (filtering vs managing our list of todos), the nesting is making the update logic harder to read, and it's not exactly clear what's going on everywhere.

**Extracting Utility Functions**[**#**](#extracting-utility-functions)

A good first step might be to break out a utility function to return a new object with updated fields. There's also a repeated pattern with trying to update a specific item in an array that we could extract to a function:

function updateObject(oldObject, newValues) {

// Encapsulate the idea of passing a new object as the first parameter

// to Object.assign to ensure we correctly copy data instead of mutating

return Object.assign({}, oldObject, newValues)

}

function updateItemInArray(array, itemId, updateItemCallback) {

const updatedItems = array.map(item => {

if (item.id !== itemId) {

// Since we only want to update one item, preserve all others as they are now

return item

}

// Use the provided callback to create an updated item

const updatedItem = updateItemCallback(item)

return updatedItem

})

return updatedItems

}

function appReducer(state = initialState, action) {

switch (action.type) {

case 'SET\_VISIBILITY\_FILTER': {

return updateObject(state, { visibilityFilter: action.filter })

}

case 'ADD\_TODO': {

const newTodos = state.todos.concat({

id: action.id,

text: action.text,

completed: false

})

return updateObject(state, { todos: newTodos })

}

case 'TOGGLE\_TODO': {

const newTodos = updateItemInArray(state.todos, action.id, todo => {

return updateObject(todo, { completed: !todo.completed })

})

return updateObject(state, { todos: newTodos })

}

case 'EDIT\_TODO': {

const newTodos = updateItemInArray(state.todos, action.id, todo => {

return updateObject(todo, { text: action.text })

})

return updateObject(state, { todos: newTodos })

}

default:

return state

}

}

Copy

That reduced the duplication and made things a bit easier to read.

**Extracting Case Reducers**[**#**](#extracting-case-reducers)

Next, we can split each specific case into its own function:

// Omitted

function updateObject(oldObject, newValues) {}

function updateItemInArray(array, itemId, updateItemCallback) {}

function setVisibilityFilter(state, action) {

return updateObject(state, { visibilityFilter: action.filter })

}

function addTodo(state, action) {

const newTodos = state.todos.concat({

id: action.id,

text: action.text,

completed: false

})

return updateObject(state, { todos: newTodos })

}

function toggleTodo(state, action) {

const newTodos = updateItemInArray(state.todos, action.id, todo => {

return updateObject(todo, { completed: !todo.completed })

})

return updateObject(state, { todos: newTodos })

}

function editTodo(state, action) {

const newTodos = updateItemInArray(state.todos, action.id, todo => {

return updateObject(todo, { text: action.text })

})

return updateObject(state, { todos: newTodos })

}

function appReducer(state = initialState, action) {

switch (action.type) {

case 'SET\_VISIBILITY\_FILTER':

return setVisibilityFilter(state, action)

case 'ADD\_TODO':

return addTodo(state, action)

case 'TOGGLE\_TODO':

return toggleTodo(state, action)

case 'EDIT\_TODO':

return editTodo(state, action)

default:

return state

}

}

Copy

Now it's _very_ clear what's happening in each case. We can also start to see some patterns emerging.

**Separating Data Handling by Domain**[**#**](#separating-data-handling-by-domain)

Our app reducer is still aware of all the different cases for our application. Let's try splitting things up so that the filter logic and the todo logic are separated:

// Omitted

function updateObject(oldObject, newValues) {}

function updateItemInArray(array, itemId, updateItemCallback) {}

function setVisibilityFilter(visibilityState, action) {

// Technically, we don't even care about the previous state

return action.filter

}

function visibilityReducer(visibilityState = 'SHOW\_ALL', action) {

switch (action.type) {

case 'SET\_VISIBILITY\_FILTER':

return setVisibilityFilter(visibilityState, action)

default:

return visibilityState

}

}

function addTodo(todosState, action) {

const newTodos = todosState.concat({

id: action.id,

text: action.text,

completed: false

})

return newTodos

}

function toggleTodo(todosState, action) {

const newTodos = updateItemInArray(todosState, action.id, todo => {

return updateObject(todo, { completed: !todo.completed })

})

return newTodos

}

function editTodo(todosState, action) {

const newTodos = updateItemInArray(todosState, action.id, todo => {

return updateObject(todo, { text: action.text })

})

return newTodos

}

function todosReducer(todosState = \[], action) {

switch (action.type) {

case 'ADD\_TODO':

return addTodo(todosState, action)

case 'TOGGLE\_TODO':

return toggleTodo(todosState, action)

case 'EDIT\_TODO':

return editTodo(todosState, action)

default:

return todosState

}

}

function appReducer(state = initialState, action) {

return {

todos: todosReducer(state.todos, action),

visibilityFilter: visibilityReducer(state.visibilityFilter, action)

}

}

Copy

Notice that because the two "slice of state" reducers are now getting only their own part of the whole state as arguments, they no longer need to return complex nested state objects, and are now simpler as a result.

**Reducing Boilerplate**[**#**](#reducing-boilerplate)

We're almost done. Since many people don't like switch statements, it's very common to use a function that creates a lookup table of action types to case functions. We'll use the `createReducer` function described in [Reducing Boilerplate](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/reducing-boilerplate.html):

// Omitted

function updateObject(oldObject, newValues) {}

function updateItemInArray(array, itemId, updateItemCallback) {}

function createReducer(initialState, handlers) {

return function reducer(state = initialState, action) {

if (handlers.hasOwnProperty(action.type)) {

return handlers\[action.type]\(state, action)

} else {

return state

}

}

}

// Omitted

function setVisibilityFilter(visibilityState, action) {}

const visibilityReducer = createReducer('SHOW\_ALL', {

SET\_VISIBILITY\_FILTER: setVisibilityFilter

})

// Omitted

function addTodo(todosState, action) {}

function toggleTodo(todosState, action) {}

function editTodo(todosState, action) {}

const todosReducer = createReducer(\[], {

ADD\_TODO: addTodo,

TOGGLE\_TODO: toggleTodo,

EDIT\_TODO: editTodo

})

function appReducer(state = initialState, action) {

return {

todos: todosReducer(state.todos, action),

visibilityFilter: visibilityReducer(state.visibilityFilter, action)

}

}

Copy

**Combining Reducers by Slice**[**#**](#combining-reducers-by-slice)

As our last step, we can now use Redux's built-in `combineReducers` utility to handle the "slice-of-state" logic for our top-level app reducer. Here's the final result:

// Reusable utility functions

function updateObject(oldObject, newValues) {

// Encapsulate the idea of passing a new object as the first parameter

// to Object.assign to ensure we correctly copy data instead of mutating

return Object.assign({}, oldObject, newValues)

}

function updateItemInArray(array, itemId, updateItemCallback) {

const updatedItems = array.map(item => {

if (item.id !== itemId) {

// Since we only want to update one item, preserve all others as they are now

return item

}

// Use the provided callback to create an updated item

const updatedItem = updateItemCallback(item)

return updatedItem

})

return updatedItems

}

function createReducer(initialState, handlers) {

return function reducer(state = initialState, action) {

if (handlers.hasOwnProperty(action.type)) {

return handlers\[action.type]\(state, action)

} else {

return state

}

}

}

// Handler for a specific case ("case reducer")

function setVisibilityFilter(visibilityState, action) {

// Technically, we don't even care about the previous state

return action.filter

}

// Handler for an entire slice of state ("slice reducer")

const visibilityReducer = createReducer('SHOW\_ALL', {

SET\_VISIBILITY\_FILTER: setVisibilityFilter

})

// Case reducer

function addTodo(todosState, action) {

const newTodos = todosState.concat({

id: action.id,

text: action.text,

completed: false

})

return newTodos

}

// Case reducer

function toggleTodo(todosState, action) {

const newTodos = updateItemInArray(todosState, action.id, todo => {

return updateObject(todo, { completed: !todo.completed })

})

return newTodos

}

// Case reducer

function editTodo(todosState, action) {

const newTodos = updateItemInArray(todosState, action.id, todo => {

return updateObject(todo, { text: action.text })

})

return newTodos

}

// Slice reducer

const todosReducer = createReducer(\[], {

ADD\_TODO: addTodo,

TOGGLE\_TODO: toggleTodo,

EDIT\_TODO: editTodo

})

// "Root reducer"

const appReducer = combineReducers({

visibilityFilter: visibilityReducer,

todos: todosReducer

})

Copy

We now have examples of several kinds of split-up reducer functions: helper utilities like `updateObject` and `createReducer`, handlers for specific cases like `setVisibilityFilter` and `addTodo`, and slice-of-state handlers like `visibilityReducer` and `todosReducer`. We also can see that `appReducer` is an example of a "root reducer".

Although the final result in this example is noticeably longer than the original version, this is primarily due to the extraction of the utility functions, the addition of comments, and some deliberate verbosity for the sake of clarity, such as separate return statements. Looking at each function individually, the amount of responsibility is now smaller, and the intent is hopefully clearer. Also, in a real application, these functions would probably then be split into separate files such as `reducerUtilities.js`, `visibilityReducer.js`, `todosReducer.js`, and `rootReducer.js`.

Previous

« Splitting Reducer Logic

Next

Using combineReducers »

**Docs**

* [Getting Started](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/getting-started.html)
* [Tutorial](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/tutorials/essentials/part-1-overview-concepts.html)
* [FAQ](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/faq.html)
* [API Reference](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/api/api-reference.html)

**Community**

* [Reactiflux Discord](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/discord.com/invite/0ZcbPKXt5bZ6au5t.html)
* [Stack Overflow](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/stackoverflow.com/questions/tagged/redux.html)
* [Feedback](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/introduction/getting-started.html#help-and-discussion)

**More**

* [GitHub](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/github.com/reduxjs/redux.html)
* [![Deploys by Netlify](https://github.com/bgoonz/Learning-Redux/blob/master/www.netlify.com/img/global/badges/netlify-color-accent.svg)](https://github.com/bgoonz/Learning-Redux/blob/master/www.netlify.com/index.html)

[![Redux Logo](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/d33wubrfki0l68.cloudfront.net/0834d0215db51e91525a25acf97433051f280f2f/c30f5/img/redux.svg) Copyright © 2015–2021 Dan Abramov and the Redux documentation authors.](https://github.com/bgoonz/Learning-Redux/blob/master/DOCS/official/index.html)
