You don’t need a Redux Starter Kit for your next project. A pragmatic approach towards building your next React Redux powered Single Page App.
Starting a React based project as of today requires quite a lot of boilerplate and tools setup – such as installing react, react-dom, babel, webpack, express and all other necessary utilties. Hence, a natural choice is start from one of the most popular starter projects. According to Andrew Farmer’s starter project search tool, there are a total of 108 starter projects as of November 22nd 2016. Almost a half of them (47) include Redux as part of the starter kit.
Dan Abramov’s Redux is a popular state container that helps you organize your application state in a predicatable way. The success of Redux has prompted many enthusiasts to eagerly include Redux into their React starter kits. While Redux helps organizing state mutations in complex applications, it might not be a early stage requirement for most projects. Similar to features being added and improved in iterations, integrating Redux should be a natural process of iterations. In most scenarios, beginning with Redux needlessly complicates the initial development.
Before looking at when and how to include Redux, let’s first take a look at how state management is done with plain React. React is architectured with state management in mind. Each React component has its local state and can pass data to child components via props. Whenever a local state is updated, React handles rerendering of the DOM automatically. This effectively implement a oneway binding. Changes of states in the parent component can propagate to the child components by passing props. For most early stages projects, this is sufficient.
In the example below, we show how a parent compoenent unidirectionally pass its states to child components.
Things only gets tricky when state synchronization is needed between siblings. In the example below, both ChildOne and ChildTwo share the same counter state. ChildOne has an additional button, which on click, increments the counter by one. Since state is stored on the parent component, ChildOne needs to update state on the parent component. The parent component then update the counter state for ChildTwo. This effectively introduce bidirectional data flow between parent component and child components, making the program harder to reason about.
Redux attempts to solve this problem by enforcing a strict unidirectional data flow, via the use of actions and reducers. State updates are decoupled as actions and reducers, which breaks any potential circular state update loop.
Redux restricts the programmers from mutating the states by decoupling the state update intent (actions) from actually performing state updates (reducers). This a powerful architecture decision. Immutable data structures are commonly perceived as easier to reason about by computer science folks. Using immutables also provides fancy artifacts such as hot reloading and time traveling.
Such powerful features do not come for free. For once, immutable data structures have a significant learning curve because they are not always intuitive. Like Eric Lippert said:
When you fire the machine gun at the alien, most people do not mentally model that as the construction of a new alien with fewer hit points; they model that as a mutation of an existing alien’s properties.
When we need to remove the i-th item from an array in Redux, we cannot simply do
array.splice(i, 1), instead we must construct a new list, without the item to be removed:
array.slice(0, i).concat(array.slice(i+1)). Unless you are coming from a functional programming background, these concepts are not easy to get used to.
After getting a clear idea of what level of state management React can provide and when does Redux shine, note that we do not need to begin with Redux. Whereas React is great for managing local states, Redux covers global states. Managing states in React is totally fine as long as the data flow is mostly unidirectional. Redux acts more like an enforcer of unidirectional data flow than an enabler. To design a global state transition system, you need to have a clear idea of how your app is going to work. When you first start building your app, there are more prototyping than actually coming up with a well defined global state structure. Starting with Redux forces you to think about global state structure, when in fact, you are not yet ready.
To this end, my suggestion would be to start with simple React and local states. As you refactor your code in the principle of speration of concerns, extract your local states synchronizations into Redux global states and adopt Redux progressively.
Redux, is, in fact all about redux.
Remember, using Redux is an architectural choice, you should adopt it only when it helps you manage code otherwise too difficult to reason about.
So start simple, you don’t need a Redux Starter Kit for your next project.
YEHEH you finished reading this article! This is the second piece of the series Pragmatic Architecture. If you enjoy these pragmatic approachs, stay tuned for more in the future!