Do you really need Redux?

Not so long ago React positioned itself as "V in MVC". After this commit the marketing text has changed, but the essence has remained the same: React is responsible for mapping, the developer for everything else, that is, speaking in terms of MVC, for Model and Controller.
One of the solutions for controlling the Model of your application is Redux. His appearance is motivated by The increased complexity of frontend applications that MVC can not handle.
Main Technical Software Development Imperative - complexity management
- The perfect code is
Redux suggests managing complexity by using predictable state changes. Predictability is achieved through three fundamental principles :
the state of the entire application is stored in one place
the only way to change the state is to send Action
all changes occur with the help of pure functions
Could Redux overcome the increased complexity and was there anything to struggle with?
Flux'om - a solution from Facebook. The reason for creating Flux is like say the developers of Facebook ( video ), Was the problem of scalability of the MVC architectural template.
According to the description of Facebook, the connection of objects in large projects using MVC eventually becomes unpredictable:
modelOne changes the viewOne to
viewOne changes its modelTwo
during its modification.  
modelTwo during its modification changes modelThree
modelThree changes its viewTwo and viewFour
during its modification.  
The problem of unpredictability of changes in MVC is also written in Redux's motivation. The picture below illustrates how Facebook developers see this problem.
Do you really need Redux?
Flux, in contrast to the described MVC, offers an understandable and slender model:
View generates Action
Action falls into the Dispatcher
Dispatcher updates Store
The updated Store notifies the View of the
View is redrawn

In addition, using Flux, several Views can subscribe to Stores of interest to them and be updated only when something changes in these Stores. This approach reduces the number of dependencies and simplifies development.

The implementation of MVC from Facebook is completely different from the original MVC, which was widely distributed in the Smalltalk world. This difference is the main reason for the application "MVC does not scale".
Back in the eighties
MVC is the basic approach to the development of user interfaces in Smalltalk-80. Like Flux and Redux, MVC was created to reduce the complexity of software and accelerate development. I will give a brief description of the basic principles of the MVC approach, a more detailed review can be read here and here .
Responsibility of MVC-entities:
Model is a central entity that simulates the real world and business logic, provides information about its state, and also changes its state upon request from Controller'a
View receives information about the status of the Model and displays it to the user
Controller monitors mouse movement, clicking on mouse and keyboard buttons and processes them by changing View or Model
And now what Facebook has missed, implementing MVC - connections between these entities:
View can be linked to
with only one
Thecontroller can be linked to
with only one
nothing knows
o View and Controller and
can not change them
View and Controller subscribe to Model
One pair of View and Controller can be signed
only for one
The model can have
a lot of subscribers
and notifies all of them after changing their state
Look at the image below. Arrows pointing from Model to Controller and View are not attempts to change their state, but alerts about changes in the Model.

The original MVC is completely different from the Facebook implementation, in which the View can change the set. Model, Model can change the View set, and Controller does not form a close one-to-one relationship with View. Moreover, Flux is MVC, in which the role of Model is played by Dispatcher and Store, and instead of calling methods, Action is sent.
React through the prism of MVC
Let's look at the code for a simple React component:
class ExampleButton extends React.Component {
render () {return (
cluster.log ("clicked!")}>
.Click Me!
. .


And now once again we turn to the description of Controller in the original MVC:

Controller monitors mouse movement, clicking on mouse and keyboard buttons and processes them by changing View or Model
Thecontroller can be linked to with only one View

Notice how the Controller entered View on the third line of the component? Here it is:

    onclick = {() => console.log ("clicked!")}    


This is the perfect Controller, which completely satisfies its description. jаvascript made our life easier by eliminating the need to independently track the position of the mouse and the coordinates in which the click occurred. Our React-components turned not just into View, but into closely related View-Controller pairs.


Working with React, we can only implement the Model. React-components will be able to subscribe to the Model and receive notifications when updating its state.


Cooking MVC


For the convenience of working with React-components, we will create our BaseView class, which will subscribe to the transmitted to the props Model:

    //src /Base /BaseView.tsx
import * as React from "react";
import BaseModel from "./BaseModel";
export default class extends React.Component {
protected model: Model;
constructor (props: any) {
super (props);
this.model = props.model
componentWillMount () {this.model.subscribe (this);}
componentWillUnmount () {this.model.unsubscribe (this);}


In this implementation, the state attribute is always an empty object, because it seemed useless. View can store its state directly in the attributes of the instance of the class and, if necessary, call this.setState ({}) , to redraw itself. Perhaps this solution is not the best, but it is easy to change, and it does not affect the essence of the article.


Now implement the BaseModel class, which provides the ability to subscribe to yourself, unsubscribe from yourself, and notify all subscribers about the status change:

    //src /Base /BaseModel.ts
export default class {
protected views: React.Component[]=[];
subscribe (view: React.Component) {
this.views.push (view);
view.setState ({});
unsubscribe (view: React.Component) {
this.views = this.views.filter ((item: React.Component) => item! == view);
protected updateViews () {
this.views.forEach ((view: React.Component) => view.setState ({}))


I implement all the known TodoMVC with the cut-down functionality, all the code can be viewed at Github .


TodoMVC is a list that contains tasks. The list can be in one of three states: "show all tasks", "show only active tasks", "show completed tasks". You can also add and delete tasks to the list. Let's create the corresponding model:

    //src /TodoList /TodoListModel.ts
import BaseModel from "/Base/BaseModel";
import TodoItemModel from "/TodoItem/TodoItemModel";
export default class extends BaseModel {
private allItems: TodoItemModel[]=[];
private mode: string = "all";
constructor (items: string[]) {
super ();
items.forEach ((text: string) => this.addTodo (text));
addTodo (text: string) {
this.allItems.push (new TodoItemModel (this.allItems.length, text, this));
this.updateViews ();
removeTodo (todo: TodoItemModel) {
this.allItems = this.allItems.filter ((item: TodoItemModel) => item! == todo);
this.updateViews ();
todoUpdated () {this.updateViews ();}
showAll () {this.mode = "all"; this.updateViews ();}
showOnlyActive () {this.mode = "active"; this.updateViews ();}
showOnlyCompleted () {this.mode = "completed"; this.updateViews ();}
get shownItems () {
if (this.mode === "active") {return this.onlyActiveItems;}
if (this.mode === "completed") {return this.onlyCompletedItems;}
return this.allItems;
get onlyActiveItems () {
return this.allItems.filter ((item: TodoItemModel) => item.isActive ());
get onlyCompletedItems () {
return this.allItems.filter ((item: TodoItemModel) => item.isCompleted ());


The task contains the text and the identifier. It can be either active or completed, and can also be removed from the list. Let's express these requirements in the model:

    //src /TodoItem /TodoItemModel.ts
import BaseModel from "/Base/BaseModel";
import TodoListModel from "/TodoList/TodoListModel";
export default class extends BaseModel {
private completed: boolean = false;
private todoList ?: TodoListModel;
id: number;
text: string = "";
constructor (id: number, text: string, todoList ?: TodoListModel) {
super (); = id;
this.text = text;
this.todoList = todoList;
switchStatus () {
this.completed =! this.completed
this.todoList? this.todoList.todoUpdated (): this.updateViews ();
isActive () {return! this.completed;}
isCompleted () {return this.completed;}
remove () {this.todoList && this.todoList.removeTodo (this)}


To the resulting models, you can add any number of View, which will be updated immediately after the changes in the Model. Add a View to create a new task:

    //src /TodoList /TodoListInputView.tsx
import * as React from "react";
import BaseView from "/Base/BaseView";
import TodoListModel from "./TodoListModel";
export default class extends BaseView {
render () {return ( ?

? type = "text"
, className = "new-todo"
placeholder = "What needs to be done?"
onkeydown = {(e: any) => {
const enterPressed = e.which === 13;
if (enterPressed) {
this.model.addTodo (; = "";


Going into such a View, we immediately see how the Controller (props onkeydown) interacts with Model and View, and which Model is used. We do not need to track the entire transmission chain of props from component to component, which reduces the cognitive load.


Let's implement another View for the TodoListModel model, which will display the list of tasks:

    //src /TodoList /TodoListView.tsx
import * as React from "react";
import BaseView from "/Base/BaseView";
import TodoListModel from "./TodoListModel";
import TodoItemModel from "/TodoItem/TodoItemModel";
import TodoItemView from "/TodoItem/TodoItemView";
export default class extends BaseView {
render () {return ( ?
{ ((item: TodoItemModel) => )}



And create a View to display one task that will work with the TodoItemModel model:

    //src /TodoItem /TodoItemView.jsx
import * as React from "react";
import BaseView from "/Base/BaseView";
import TodoItemModel from "./TodoItemModel";
export default class extends BaseView {
render () {return ( ?

? ?

? ?

? type = "checkbox" ? className = "toggle"
checked = {this.model.isCompleted ()}
onchange = {() => this.model.switchStatus ()}
. {this.model.text} .
. this.model.remove ()} />


TodoMVC is ready. We used only our own abstractions, which took less than 60 lines of code. We worked at the same time with two moving parts: Model and View, which reduced the cognitive load. We also did not encounter the problem of tracking functions through props, which quickly turns into hell. And we did not have to create Fake Container components .


What's wrong with Redux?


I was surprised to find the stories from negative experience using Redux problematic, because even the author of the library says , that Redux is not suitable for all applications. If your frontend application must:

  • be able to save your state in local storage and start using the saved state  
  • be able to fill your state on the server and transfer it to the client inside HTML  
  • Transfer Action on the network  
  • support undo state of the application  


That can choose Redux as a pattern of working with the model, otherwise it is worth considering the appropriateness of its application.


Redux is too complicated, and I'm not talking about the number of lines of code in the library's repository, but about those approaches to software development that he preaches. Redux erects indirection in the absolute is , suggesting that you start developing an application with Presentation Components and transfer everything, including Action for changing State, through props. A large number of indirections in one place makes the code complex . And the creation of reusable and configurable components at the beginning of development leads to premature generalization of , which makes the code even more difficult to understand and modify.


For demonstration indirection'ov you can look at the same TodoMVC, which is located in the official repository Redux. What changes in the State application will occur when calling the callback onSave , and in what case will they occur?

In the absence of the desire to arrange an investigation yourself, you can look under the spoiler [/b]
hadleSave of TodoItem it is transmitted as props onSave in TodoTextInput  
onSave it is called when is pressed. Enter or, if no props are transmitted newTodo , on the action onblur  
hadleSave calls props deleteTodo , if the note has changed to an empty string, or props editTodo otherwise  
props'y deleteTodo and editTodo fall into TodoItem of MainSection  
MainSection just proxy the received props' deleteTodo and editTodo in TodoItem  
props'y in MainSection fall from the container App with the help of bindActionCreator , and therefore are dispatching action'ov from src /actions /index.js , which are processed in src /reducers /todos.js
And this is a simple case, because the callbacks received from props are turned into additional functionality only 2 times. In a real application, you can face a situation where there are more such changes.
When using the original MVC, it's much easier to understand what happens to the application model. The same change of note does not contain unnecessary indirections and encapsulates the entire logic of the change in the model, and does not spread it over the components.
The creation of Flux and Redux was motivated by MVC's non-scalability, but this problem disappears if you use the original MVC. Redux tries to make the change in the state of the application predictable, but the waterfall from the callbacks in the props does not only not contribute to this, but it also brings you closer to losing control of your application. The increased complexity of frontend applications, mentioned by the authors of Flux and Redux, was not. It was just a misuse of the approach to development. Facebook itself created the problem and itself heroically decided it, announcing to the whole world about a "new" approach to development. B
The bulk of the frontend community followed Facebook, because we used to trust the authorities . But maybe it's time to stop for a moment, take a deep breath, discard the HYIP and give the original MVC another chance?
Should I use the original MVC instead of Redux?
You can try
The essence of the original MVC remained unclear
12 users have voted. Abstained 4 users.
Only registered users can participate in the survey. Enter , you are welcome.
+ 0 -

Add comment