Best Practices for Building JavaScript Applications with React and MobX (Part 1 of 3)

November 01, 2017 in Web Development Articles

Written by Eric Greene


Introduction

When building applications, there are many ways of solving any problem. Mastering a toolset is more than getting it to work, it’s about getting it to work correctly.

Many times, while I am teaching classes and eagerly helping students do their lab exercises, the students will work very hard and creatively to solve a problem, and usually they solve it. Unfortunately, they sometimes solve it by violating core best practices of the tools or patterns being used.

When first learning a new technology, breaking best practices may not seem like a major concern to the student, but their lack of production experience with the technologies has not yet revealed to them the perils of breaking key principles that are in place to ensure the application is built correctly, regardless of size and purpose of the application.

JavaScript is a great language. It is flexible, extensible, and very expressive. Such qualities also lend it to being used in ways it should not be. These qualities translate into errors in coding when using many JavaScript libraries, including React and MobX. The following 3-part series will present 7 Best Practices recommended for building applications with JavaScript, React, and MobX. Each post in the series will have 2-3 Best Practices described in great detail.

Best Practice #1 – Declaring Variables

Before ES2015 (aka ES6), the only way to declare variables was by using the var keyword. Variables declared with var were mutable, meaning their values could be changed. With the advent of ES2015, two new keywords were defined for declaring variables: const and let.

const declares immutable variables and let declares mutable variables. This distinction (and a few other unrelated features of these two keywords) have greatly benefited JavaScript. They allow a clearer intent of the purpose and usage of the variables within a JavaScript application. Many times, my students will ask me the preferred way to declare variables: is it var, const, or let?

Variables for new code should never be declared with var. When working with old code, var declarations should be replaced with const or let, as appropriate. There is no good use case for using var, and while it is still supported by JavaScript engines for backward compatibility, it should never be used. Nevertheless, because there is so much legacy code still in use today, every JavaScript developer should know the details and implications of using var, such as declaration hoisting and function scope.

Since var will not be used, should const or let be used? Answer: all variables should be declared with const unless they will be intentionally mutated. Many developers mentally equate const with the compile-time constants found in other languages such as C or C++. The JavaScript const is not a compile-time constant; it is a run-time constant. Using const communicates to other developers the intended immutable purpose of the variable and helps to clarify the flow of data. Variables that will never change, which will be most of them, should be configured as such.

The keyword let should only be used in situations where the mutation is intentionally planned. Examples include incrementing a counter or declaring a variable above an if or switch block and assigning a value based upon the control flow where the value will be used after the control block has completed.

// do not use
var a = 2;

// variable is immutable
const b = 2;
b = 3; // throws an error

// variable is mutable
let c = 2;
c = 3; // allowed

It’s good to keep in mind that JavaScript is primarily a functional programming language, and most functional languages leverage immutable variables, not mutable variables. Even in the examples above for mutating, functional programmers would argue for more functional approaches to structures instead of if and switch control flow blocks.

Best Practice #2 – Immutable Programming with Objects

One of the many challenges of user interface programming is solving the problem of change detection. There are many strategies for solving this issue, including:

  1. Do not detect changes and re-render everything
  2. Detect changes through value and object reference comparison
  3. Detect Changes through tracking property access

By default, the React library uses the first strategy. It’s easy to implement, but inefficient. For relatively small component trees, it works fine, and any inefficiencies are not noticed by the user.

Note: Re-render does not necessarily mean re-rendering the real DOM tree. With React, the re-rendering discussed here is the in-memory Virtual DOM tree. The updates to the real DOM still occur through the optimized reconciliation process, meaning the real DOM tree is not necessarily completely re-rendered and is only updated as needed. While React, by default, inefficiently updates the Virtual DOM, it always updates the real DOM efficiently.

The second strategy is employed by React’s Pure Component and state management libraries such as Redux. It is efficient for performing change detection, but requires developers to faithfully work with objects (including arrays) using immutable programming techniques. The “faithfully” part is key here because, in many cases, there is no effective or efficient coding mechanism to ensure immutable programming techniques are being used. Code reviews and proper developer training is critical.

The third technique is used by state management libraries such as MobX. This technique requires less work on the part of the developer, but it employs more “magic” to perform the tracking. It can break if developers do not work with objects (and specifically the object’s properties) correctly.

Note: We all know there is no such thing as magic when it comes to programming. All code is logical, and the processor executes the code logically. By “magic,” this means the logic is not clear to the average developer and can result in mistakes which were not obvious. The mistakes are not violations of JavaScript itself, but rather violations of the stricter rules about the “magic” code. Debugging the errors caused by these violations is especially difficult because the developer does not understand what they did wrong because the code’s limitations are not clear.

In general, using immutable programming techniques in JavaScript applications for working with objects (including arrays) is a good idea. Normally, if objects are treated as immutable, then the application can leverage the performance enhancements of detecting changes more quickly. Fortunately, most of the tools used in JavaScript to work with objects immutably are built into JavaScript itself, which means they will be very fast at run-time.

Note: It is always better to use built-in JavaScript APIs than to use the equivalent library written in JavaScript. JavaScript APIs will execute your code and process your data with highly optimized C++ code at a low-level in the JavaScript engine. Writing a similar function in JavaScript will always run more slowly. For example, the Promise API built into JavaScript is always better than the Promise API implemented in pure JavaScript and included as a JavaScript library. Only transpile back to the version of JavaScript you must support. Going back too many versions will replace efficient code understood by modern JavaScript engines with inefficient polyfills that mimic a new built-in feature. If you are using Babel the new environment configuration option where the supported browsers are specified is a great way to only transpile what you need while keeping more of your code using the newer native features.

Working with objects immutably is relatively simple. For example, instead of using push, pop, shift, unshift and slice to add and remove items from an array, the immutable functions slice and concat should be used.

// mutable operations
const nums = [ 1, 2, 3, 4 ];

// array mutated to [ 1, 2, 3, 4, 5 ]
nums.push(5);

// array mutated to [ 1, 2, 3, 4 ]
nums.pop();

// array mutated to [ 1, 2, 9, 4 ]
nums.splice(2, 1, 9);
// immutable operations
const oldNums = [ 1, 2, 3, 4 ];

// oldNums: [ 1, 2, 3, 4 ]
// newNums: [ 1, 2, 3, 4, 5 ]
let newNums = oldNums.concat(5);

// oldNums: [ 1, 2, 3, 4 ]
// newNums: [ 2, 3 ]
newNums = oldNums.slice(1,3);

Also, the array spread and rest operators can be used to produce new arrays when adding and removing items.

// immutable operations with spread and rest
const oldNums = [ 1, 2, 3, 4 ];

// oldNums: [ 1, 2, 3, 4 ]
// removeItem: 1
// newNums [ 2, 3, 4 ]
const [ removeItem, newNums ] = oldNums;

// oldNums: [ 1, 2, 3, 4 ]
// newNums [ 1, 2, 3, 4, 5 ]
const newNums = [ ...oldNums, 5 ];

When updating object properties, the function Object.assign can be used. If a project is using Babel or TypeScript to transpile the JavaScript code, the object spread operator can be used in place of Object.assign.

// Examples of Object.assign and Object Spreads
const oldPerson = {
  firstName: 'Bob',
  lastName: 'Smith',
};

// oldPerson => { firstName: 'Bob', lastName: 'Smith' }
// newPerson => { firstName: 'Jane', lastName: 'Smith' }
let newPerson = Object.assign({}, oldPerson, { firstName: 'Jane' });

// oldPerson => { firstName: 'Bob', lastName: 'Smith' }
// newPerson => { firstName: 'Jane', lastName: 'Smith' }
newPerson = { ...oldPerson, firstName: 'Jane' };

One of the great challenges of immutably working objects is updating an object deep within an object tree. When an object is updated deep in the tree, a new object is needed for that object, as well as every ancestor object above it in the tree. It’s important to keep the object tree from growing too deep, otherwise performing immutable operations is very painful.

Several libraries exist to help with immutable operations. One of the more popular ones is Immutable.js, provided by Facebook.

Conclusion

The “best” best practice of any React or MobX developer is to deeply learn JavaScript. Most students have few problems with learning React or MobX but struggle greatly with the JavaScript used to code with both technologies. In this post, two specific best practices regarding variables and immutable objects were examined. In future posts, best practices regarding React components and managing application state with MobX will be explored. Do you have any best practices from your experiences? Do you disagree with any of the practices listed in this blog post? If so, please tell us in your comments below.

Continue to Part 2


Accelebrate offers private React/Redux training for groups and instructor-led online JavaScript classes for individuals.


Written by Eric Greene

Eric Greene

Eric is a professional software developer specializing in HTML, CSS, and JavaScript technologies. He has been developing software and delivering training classes for nearly 19 years. He holds the MCSD Certification for ASP.Net Web Applications, and is a Microsoft Certified Trainer.


Learn faster

Our live, instructor-led lectures are far more effective than pre-recorded classes

Satisfaction guarantee

If your team is not 100% satisfied with your training, we do what's necessary to make it right

Learn online from anywhere

Whether you are at home or in the office, we make learning interactive and engaging

Multiple Payment Options

We accept check, ACH/EFT, major credit cards, and most purchase orders



Recent Training Locations

Alabama

Birmingham

Huntsville

Montgomery

Alaska

Anchorage

Arizona

Phoenix

Tucson

Arkansas

Fayetteville

Little Rock

California

Los Angeles

Oakland

Orange County

Sacramento

San Diego

San Francisco

San Jose

Colorado

Boulder

Colorado Springs

Denver

Connecticut

Hartford

DC

Washington

Florida

Fort Lauderdale

Jacksonville

Miami

Orlando

Tampa

Georgia

Atlanta

Augusta

Savannah

Hawaii

Honolulu

Idaho

Boise

Illinois

Chicago

Indiana

Indianapolis

Iowa

Cedar Rapids

Des Moines

Kansas

Wichita

Kentucky

Lexington

Louisville

Louisiana

New Orleans

Maine

Portland

Maryland

Annapolis

Baltimore

Frederick

Hagerstown

Massachusetts

Boston

Cambridge

Springfield

Michigan

Ann Arbor

Detroit

Grand Rapids

Minnesota

Minneapolis

Saint Paul

Mississippi

Jackson

Missouri

Kansas City

St. Louis

Nebraska

Lincoln

Omaha

Nevada

Las Vegas

Reno

New Jersey

Princeton

New Mexico

Albuquerque

New York

Albany

Buffalo

New York City

White Plains

North Carolina

Charlotte

Durham

Raleigh

Ohio

Akron

Canton

Cincinnati

Cleveland

Columbus

Dayton

Oklahoma

Oklahoma City

Tulsa

Oregon

Portland

Pennsylvania

Philadelphia

Pittsburgh

Rhode Island

Providence

South Carolina

Charleston

Columbia

Greenville

Tennessee

Knoxville

Memphis

Nashville

Texas

Austin

Dallas

El Paso

Houston

San Antonio

Utah

Salt Lake City

Virginia

Alexandria

Arlington

Norfolk

Richmond

Washington

Seattle

Tacoma

West Virginia

Charleston

Wisconsin

Madison

Milwaukee

Alberta

Calgary

Edmonton

British Columbia

Vancouver

Manitoba

Winnipeg

Nova Scotia

Halifax

Ontario

Ottawa

Toronto

Quebec

Montreal

Puerto Rico

San Juan