15
2017
Productive Development With React Redux
Introduction to Redux and React
React is a JavaScript library and not a framework which mainly focuses on User Interface .It has a one way reactive data flow that forces you to think in terms of components. This model of thinking fits user interfaces well and the concept of state and where it belongs must be carefully thought of.
State management is the critical and difficult situation although it has many solutions. Redux gives the great support to handle the State Management in user interface.
Redux was created by Dan Abramov around June 2015. It was inspired by Facebook’s Flux and functional programming language Elm.
Redux is a framework that controls states in a JavaScript app. According to the official site:“Redux is a predictable state container for JavaScript apps.”
Redux is usually used for application state management. Redux maintains the state of a whole application in a single immutable state tree (object), which can not be changed directly. When something changes, a new object is created.
Figure 1 : Without Redux and with Redux application state behavior (Source: https://www.slideshare.net/binhqdgmail/006-react-redux-framework)
Suppose ‘A’ component has an attribute of user name. When the state of the username is changed, it should be reflected to the other components(‘B’). In this situation, the data has to be retrieved through an API call from the back end or a separate method has to be called to change the state of username in ‘B’ component.
A separate store can be maintained to keep the state of the user name and the state can be retrieved from the store to ‘B’ component when using Redux.
Advantages of Using Redux?
1. Predictability of outcome
Syncing the current state with actions and other parts of the application is simple and straightforward since the correct information is always available in store.
2. Maintainability
Because of the predictable outcomes and strict structure, maintainability is easy.
3. Organization
Redux is highly concentrated about how code should be organized ,which make more consistent and flexible for a team to work with.
4. Server rendering
This is very useful, especially for the initial render, making for a better user experience or search engine optimization.It can be achieved by passing the store created on the server to the client side
5. Developer tools
Developers can track everything going on in the app in real time, from actions to state changes.Chrome has redux devtools. Developers can easily debug and catch on changes or errors.
6. Ease of testing
Unit testing can be done via redux.Generally,test cases are written for small functions that are independent and only capable of doing one thing and the code is mostly functions small, pure and isolated.
Data Flow Across Components With and Without Redux
Redux is a container for managing both data-state and UI-state in JavaScript applications. It is also a framework-agnostic, so while it was written with React in mind, it can even be used with Angular or a jQuery application.
As the requirement for JavaScript application have become increasingly complicated, the code must manage more state in order to facilitate the high interactive complex UIs. Since React flows data through components in an unidirectional way from parent to child, it becomes a huge challenge to manage state throughout the multiple components. As an example,show below is how two non-parent child components would communicate in React.
Figure 2 : Without Redux – Poor practice when component-to-component communicate that doesn’t have parent to child relationship
React does not recommend direct component-to-component communication this way (that does not have parent to child relationship). React has features to support this approach,however, it is considered poor practice by many because it is error prone and leads to spaghetti code – an old term for code that is hard to follow.
React doc states it as follows:
“For communication between two components that don’t have a parent-child relationship, you can set up your own global event system. … Flux pattern is one of the possible ways to arrange this.”
This is where Redux comes in handy. Redux offers a solution of storing all application state in one place, called a “store”. Components then “dispatch” state changes to the store, not directly to other components. The components that need to be aware of state changes can “subscribe” to the store.
Figure 3 : With Redux components are dispatching and subscribing state from the store
The store is acting as the “middleman” for managing state of the application. With the involvement of Redux, components do not need to communicate with each other, but rather all state changes must go through the single source of truth, the store.
Main Guiding Principles of Redux
Redux can be described in three fundamental principles:
1. Single source of truth
The state of the whole application is stored in an object tree within a single store. Since all state resides in one place, Redux calls this the single source of truth.
The data structure of the store is ultimately up to the developer and it will help to develop universal applications. Single state of tree also makes it easier to debug or inspect an application.
Some functionality which has been traditionally difficult to implement – Undo/Redo, for example can suddenly become trivial to implement, if all of your state is stored in a single tree.
2. State is read-only
In Redux only way to change the state is to emit and action, an object describing what happened.
This ensures the application cannot modify the state directly. Instead, Redux dispatches an “actions” to express an intent to change the state in the store. As actions are just plain objects which can be logged, stored and later replayed for debugging or testing purposes.
The store object provides API with the following four methods:
- store.dispatch(action)
- store.subscribe(listener)
- store.getState()
- replaceReducer(nextReducer)
So as mentioned there’s is no method for setting state and dispatching an action is the only way to change the state in store.
var action = { type: 'ADD_USER', user: {name: 'Bravo'} } // Assuming a store object has been created already store.dispatch(action);
The dispatch() method sends an object to Redux, as an action which hold the type and all other data that need to update the state in store. The design and the content of the action object is totally up to the developer and the situation.
3. Changes are made with pure functions
As discussed Redux does not allow the application to make any direct state changes. Instead it needs to dispatch actions by describing what needs to be changed.
Therefore, Redux uses pure functions (pure reduces) to update the state tree. The term of “pure function” can be described as follows:
- The function always evaluates the same result value given that the argument remains the same.
- The function cannot depend on any state or value that may change during the execution of the function.
- It does not make any outside API or database calls.
Reducers are just pure functions that take the previous state and an action, and return the next state. Important thing is here reducers always need to return new state objects, instead of mutating the previous state.The figure below describes the architectural data flow design when using redux with react.
Figure 4 : Architectural design of connection react with redux
Introducing Actions and Reducers Redux
Actions are plain JavaScript objects.It must have a ‘type’ property that illustrates the type of action being performed. It means that identifies to the consumer the nature of the action that has occurred. ‘type’ is usually a string constant or a Symbol. If two types are the same, they MUST be strictly equivalent (using ===).
const SHOW_GREEN= 'SHOW_GREEN' { type: SHOW_GREEN, text: 'It says GO' }
You can design the object which can uses more attributes rather than type . Those are error, payload and meta property.
The ‘payload ’ property may be any type of value. It represents the payload of the action. Any information about the action that is not the type or status of the action should be part of the payload field.For example, if some values are to be passed as parameters to API call , they can be added into the ‘payload ’section.
The ‘meta’ property may be any type of value. It represents any extra information that is not part of the payload.
The ‘error’ property may be set to true if the action represents an error.
An action whose error is true is analogous to a rejected Promise. By convention, the payload should be an error object.
If error has any other value besides true, including undefined and null, the action must not be interpreted as an error.
By convention, if error is true, the payload should be an error object. This is akin to rejecting a promise with an error object.
Action Creators
Action creators is a plain function that returns an action object.
function showGreen() { return { type: SHOW_GREEN }; }
Are Action Creators Mandatory?
It is upto your application design. Suppose the application has an action which has been used in several places, it is beneficial to implement action creators, whereas if there is an action which has been used only once throughout the application, then it is a better choice to implement actions rather than action creators.
Action Creators are used to keep consistency and maintainability,because everything is abstracted into a function.
For simple apps or those with simple actions, this is a perfectly reasonable option. Sometimes are just not worth the effort,so it will be least boilerplate.
Reducers
Reducers are pure functions that takes the previous state then an action and returns the next state.It means reducers are consider about “what store was before “ then they listen for actions and dispatch something to listening components respectively. Finally will combine all reducers,then keeps tabs of anything happening to the entire state of the application.
The important thing to keep in mind is that reducers do not mutate the state. In fact, it clones a copy of the state and applies the new changes instead.
It is important to keep in mind that it’s arguments are never mutate inside a reducer and also never performs side effects like API calls and routing transitions inside the reducer.It is not advisable to call non-pure functions such as Date.now() or Math.random() inside of the reducer.
Store
The store is not a class ,It is just an object holding a few methods. A store holds the whole state tree of an application and the only way to change the state inside it is to dispatch an action on it.There should only be one store for the whole application. When the application is growing ,it is good to keep a single store with a single root reducing function, instead of adding stores. Root reducers are split into smaller reducers, independently operating on the different parts of the state tree.
According to figure 4 API method get/post, set/retrieve data with connection action creators and then connect with reducer and store, get the new state changes and connect with the user interface
Best Practices
In Redux-React development care must be taken when enforcing good patterns, as you might find yourself regretting the decision to use Redux. Following are some best practices that should be considered:
Redux Practices
- Do not mutate state, copy it, using immutable data structures
The code used will be easily understood if the component state is immutable. This way, the state changes are more explicit and easy to test. Based on state, React renders the user interface and if state is mutated React re-renders the component. If the state is mutated directly, it will be very difficult to debug and test the state change. Therefore, do not mutate it, instead, create a changed copy of it.
A reducer is the only place that changes the application’s state. It receives the old state and an action as inputs and returns the next state. A reducer should never mutate its input. If this is attempted, Redux will provide a warning that this is not a reducer’s intended function. It is very rare that the old state in reducers is mutated and using an immutable object to represent the state is not necessary. This concept should be followed by a team. If .push() method is used, the state is then mutated, instead, use .concat() to make a copy of array. But what if the state is mutated outside of a reducer? There is a possibility of mutating the state by mistake. Redux Devtools can be used to find where the state of the application has gone wrong, however, this does not help in identifying why and or where it was mutated.
Immutable.js library helps to reduce this issue because with this the state tree cannot be mutated in any point of the code. Every operation performed on the state object will retrieve a copied instance, instead of changing the current one. Also in JavaScript, List, Maps etc. are mutable. As it is difficult to handle immutability in JavaScript, using libraries such as immutable.js makes development easier. Also using immutable data leads to garbage thrashing if data structures are constantly cloned when performing mutation, but with ImmutableJS it returns new reference without cloning the underlying data.
- Centralize state: Single source of truth
App state can be easily maintained if all state is stored in one place. This is called a single source of truth. Debugging and development becomes easier and faster in this way. If the single source of truth rule is not adhered to, the following benefits will be lost:
- Deterministic view render
- Deterministic state reproduction
- Time travel debugging
- Easy testability
- Avoid Nested state
It is considered to be better to have the state value as flat instead of a tree structure. If the state is nested it will be difficult to check whether the state is changed or not. To update state, if the state is nested, React needs to make a lot of value equality checks. By making state simple the performance of react can be optimized.
- Move logic to Actions Creator instead of Reducer, Reducers must be pure
Reducer should only update the state; there should be no logic in reducers. Reducers should be simple and pure. Pure functions will have following characteristics:
- No outside network or database calls.
- Return value only depends on its parameters.
- Arguments should not be changed, immutable.
- When pure functions are called with same set of arguments, it always returns same values.
Business logic belongs to action creators. Redux sagas or redux thunk can be used in Action creators.
- Action Strategies
Using String literals is a good way to distinguish between action types, however, whether they are unique globally must be ascertained. To ensure uniqueness it is recommended to make action types as constants. This can be achieved through the use of action creators, a function which creates and returns action objects.
React Best Practices
- React’s Synthetic Events
React event object is wrapped in a Synthetic Event object. The objects received at an event handler will be reused for other events to increase performance. So accessing the event object’s properties asynchronously will be impossible since the event’s properties have been reset due to reuse.
This issue can be avoided by storing the event’s property inside its own binding.
- Stateless Component
It is recommended to have more stateless components in app. It will increase reusability of the component. It is also easy to test and debug a stateless component.
- Imports
It is good to import a specific function from the file instead of full libraries. This way the size of the application can be reduced.
Instead of
- Bind the functions in the constructor method
Calling bind or arrow functions in render as shown below has performance implications since it creates a brand new function on every single render. It is recommended to bind the functions in constructor method. This anti pattern can be caught using eslint-plugin-react.
- Avoid using variable directly in component
Avoid using variable directly. React treats [ ] this as a new variable and will re – render component on any change. This will destroy every pure render optimization. Instead it can be defined outside and used. Also use getDefaultProps instead of defining a variable.
- Adding Getters
If the prefix get is added before functions, It will eliminate the extra function execution with every call to get.
Usage of the Redux with React
following section describes the simple user creation process with React and Redux application. Here it shows the important code sections and how your code separates to the different sections such as component, action, logic and reducer using simple user created example. Basically, the UI component contains the presentational forms of the application and it will invoke the relevant actions through the logic middleware mapping. According to the Redux documentation, actions are payloads of information that send data from your application to store. So, in action function, it will dispatch an action with a defined action type. According to the dispatched action type, relevant logic functions will be executed. Logic or the logic-middleware listens to the action type and will perform the necessary business logic. Then the reducer will update the store with updated data-state and UI-state accordingly.
Create user component(UserForm.jsx)
User related actions(UserAction.js)
User related logic(UserLogic.js)
User related reducers(UserReducer.js)
Tools & Technologies
Developing React-Redux applications is a little more complex. There are powerful tools available to make the process productive.
Chrome’s React/Redux Developer Tools
Debugging Javascript is very complex, especially if it is a React app. React Developer Tools is a tool that allows you to inspect a React Renderer, including the Component hierarchy, props, state, and much more. It is a very important tool in your development.
- Arrow keys for navigation
- Right click a component to show in elements pane, scroll into view, show source,
- Use the search bar to find components by name
- A red collapser means the component has state/context
The Ducks’s File Structure for Redux
Ducks helps to package all the related code into a Redux module. Each module contains all of its related constants, actions/action creators, and its reducer. If other modules need this module, it can be imported or exported. There are some rules that needs to be followed when writing Ducks module:
- Must export a default function called reducer()
- Must export its action creators as functions
- Must have action types in the form npm-module-or-app/reducer/ACTION_TYPE
- May export its action types as UPPER_SNAKE_CASE, if an external reducer needs to listen to them, or if it is a published reusable library
Webpack
Webpack is a powerful tool which provides options for minification and sourcemap for your bundle. Also it can be run as middleware through a custom server called webpack-dev-server, which supports both live reloading and hot reloading. But major drawback with webpack is that it is difficult to configure. By injecting a global variable named process.env.NODE_ENV. with webpack, it can be made certain that the output bundle is performance optimized and error messages are stripped away.
new webpack.DefinePlugin({ ‘process.env.NODE_ENV’, JSON.stringify(‘production’) })
Eslint
Eslint can be used to show errors and warnings directly in the editor and it will also fix those errors automatically.
React Hot Loader
React hot loader helps for live reloading of the code. When the code is edited and saved, the browser will automatically get the new changes without reloading it manually. Most important thing is React components are reloaded without losing the Redux state.
Editors, IDEs
Atom, Nuclide, Webstorm, Visual studio code are some available editors/IDE for React-Redux application development. Visual studio code is free and open source and it supports debugging, embedded Git control and more features. It is developed by microsoft for Windows as well as for Linux. Atom is a text editor which is used by developers for most of the technologies. It has an active community, so plugins and enhancements will always be available. Sublime Text is also another editor with a huge active community and used by more developers. Webstorm is built on top of open source intellij platform, it supports code completion, on the fly code analysis, code formatting, refactoring..etc.
Unit Testing
One of the last challenges faced in React Redux development is unit testing. Unit testing will help to ensure that the code behaves as expected.When working in a team, code base gets bigger, it is impractical to manually check if every function or class behaves as expected. Writing automated tests to check whether the code is working properly whenever the code is changed, will save time, which can be spent on debugging.Jest is one of the recommended test engines for React Redux applications. Mocha and Jasmine are some other available testing frameworks.Listed below are some of the advantages of using Jest:
- Minimum time to setup the project
- Ability to run tests in parallel where Mocha does not have this ability
- Snapshot testing which helps to reduce number of tests to write
- In-built Manual mocking
- Code coverage comes right out of the box
When writing unit test for React-Redux application, unit testing can be broken down in the following manner:
- Presentational components: to test rendering and event callbacks
- Container components: to test rendering and Redux connections
- Actions: to test whether expected data is returned
- Reducers: to test logic and check whether reducers are pure or not.
- End to End test: to test rendering the root component and simulation of user interactions.
Conclusion
The number of developers using React and Redux nowadays for their web application development is increasing. By following best practices and using available tools and technologies developers can make their development much faster and more productive with an updated quality of code.
References
- http://redux.js.org/
- https://medium.com/front-end-developers/react-redux-tutorial-d1f6c6652759
- https://css-tricks.com/learning-react-redux/
- http://nalwaya.com/javascript/2016/05/02/react-js-best-practices.html
- https://getstream.io/blog/react-redux-best-practices-gotchas/
- https://medium.com/@scbarrus/the-ducks-file-structure-for-redux-d63c41b7035c
- https://medium.com/javascript-scene/10-tips-for-better-redux-architecture-69250425af44
- https://www.codementor.io/mz026/getting-started-with-react-redux-an-intro-8r6kurcxf
- https://teropa.info/blog/2015/09/10/full-stack-redux-tutorial.html
Authors
- Lahiru Yapa
- Vithiya Sivapalan
- Thanuja Pubudini
On this Page
- Introduction to Redux and React
- Advantages of Using Redux?
- Data Flow Across Components With and Without Redux
- Main Guiding Principles of Redux
- Introducing Actions and Reducers Redux
- Reducers
- Store
- Best Practices
- React Best Practices
- Usage of the Redux with React
- Tools & Technologies
- Chrome’s React/Redux Developer Tools
- The Ducks’s File Structure for Redux
- Related Post