Chapter 7. React Addons

In the previous chapter, we learned to use React on the server side. We understood pre-rendering of the React components and changes in the component life cycle when using React on server. We also saw how to use server-side API of React using Express.js.

In this chapter, we will look at React addons—utility packages that are not a part of React core, however, they make development process fun and enjoyable. We will learn to use immutability helpers, cloning of components, and test utilities in this chapter. We will not be covering other addons such as Animation, Perf, and PureRenderMixin. These addons will be covered in the following chapters.

In this chapter, we will cover the following topics:

  • Getting started with React addons
  • Immutability helpers
  • Cloning React components
  • Test helpers

Getting started with Addons

After completing the previous project about using React on server side, Mike's team got some free time before starting the next project. Mike decided to utilize this time by learning about React addons.

"Shawn, we got some free time. Let's use it to get started with React addons."

"What are React addons? Are they related to React core library?" Shawn asked.

"React addons are utility modules that are not a part of the React core library. However, they are blessed by the React team. In future, some of them might be included in the React core. These libraries provide helpers for writing immutable code, utilities for testing React apps, and ways to measure and improve the performance of React apps." explained Mike.

"Each addon has its own npm package, making it to simple to use. For example, to use the Update addon, we need to install and require its npm package."

  $ npm install  react-addons-update --save

  // src/App.js
  import Update from 'react-addons-update';

Immutability helpers

"Shawn, as we are learning about addons, let's add the sorting feature to our app so that the users can sort the books by their titles. I have already added the required markup for it."

Immutability helpers

"Can you try writing the code for sorting when a user clicks on the Title heading?" Mark asked.

"Here it is. I introduced the sorting state to indicate the direction of sorting—ascending or descending."

  // Updated getInitialState function of App component
  // src/App.js

  getInitialState(){
    return { books: [],
             totalBooks: 0,
             searchCompleted: false,
             searching: false,
             sorting: 'asc' };
  }

"When the user clicks on Title, it will sort the books stored in the existing state in ascending or descending order using the sort-by npm package and update the state with the sorted books."

import sortBy from 'sort-by';

_sortByTitle() {
    let sortByAttribute = this.state.sorting === 'asc' ? "title" : "-title";
    let unsortedBooks = this.state.books;
    let sortedBooks = unsortedBooks.sort(sortBy(sortByAttribute));
    this.setState({ books: sortedBooks, 
                    sorting: this._toggleSorting() });
  },

  _toggleSorting() {
    return this.state.sorting === 'asc' ? 'desc' : 'asc';
  }

"Shawn, this is functional; however, it's not following the React way. React assumes that the state object is immutable. Here we are using reference to the books from the existing state when we are assigning value to unsortedBooks."

let unsortedBooks = this.state.books;

"Later, we are mutating unsortedBooks into sortedBooks; however, as a side-effect, we are also mutating the current value of this.state."

_sortByTitle() {
    let sortByAttribute = this.state.sorting === 'asc' ? "title" : "-title";
    let unsortedBooks = this.state.books;
    console.log("Before sorting :");
    console.log(this.state.books[0].title);
    let sortedBooks = unsortedBooks.sort(sortBy(sortByAttribute));
    console.log("After sorting :");
    console.log(this.state.books[0].title);
    // this.setState({ books: sortedBooks, 
                       sorting: this._toggleSorting() });

  },
Immutability helpers

"As you can see, even if we commented call to this.setState, our current state is still mutated." Mark explained.

"This can be easily fixed using Object.assign from ES6. We can simply create a new array and copy the current value of this.state.books in it. We can then sort the new array and call setState with the new sorted array." informed Shawn.

Note

The Object.assign method copies the values of all the enumerable properties from multiple source objects in a target. More details can be found at the following: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign.

_sortByTitle() {
    let sortByAttribute = this.state.sorting === 'asc' ? "title" : "-title";
    let unsortedBooks = Object.assign([], this.state.books);
    console.log("Before sorting :");
    console.log(this.state.books[0].title);
    let sortedBooks = unsortedBooks.sort(sortBy(sortByAttribute));
    console.log("After sorting :");
    console.log(this.state.books[0].title);
    this.setState({ books: sortedBooks, 
                    sorting: this._toggleSorting() });
  }
Immutability helpers

"Yes. This works. But the Object.assign method will make a shallow copy of this.state.books. It will create a new unsortedBooks object, however, it will still use the same references from this.state.books in unsortedBooks. Let's say, for some reason, we want the titles of all books in uppercase letters, then we may accidently mutate this.state too," Mike explained.

_sortByTitle() {
    let sortByAttribute = this.state.sorting === 'asc' ? "title" : "-title";
    let unsortedBooks = Object.assign([], this.state.books);
    unsortedBooks.map((book) => book.title = book.title.toUpperCase());
    console.log("unsortedBooks");
    console.log(unsortedBooks[0].title);
    console.log("this.state.books");
    console.log(this.state.books[0].title); 
  }
Immutability helpers

"As you can see, even after using Object.assign, this.state.books was still mutated. Actually, this has nothing to do with React as such. It is due to the way JavaScript passes references of arrays and objects around. However, due to this, if we have arrays and objects in our deeply nested state, it becomes hard to prevent mutations." Mike further explained.

"Do we always have to perform a deep copy of the state object to be on the safer side?" Shawn asked.

"Well, deep copies can be expensive and sometimes hard to achieve with deeply nested state. Fortunately, React provides the Update addon with immutability helpers, which we can use to solve this issue." added Mike.

"While using immutability helpers, we need to answer the following three questions:"

  • What needs to be changed?
  • Where it needs to be changed?
  • How it needs to be changed?

"In this case, we need to change the this.state to display the sorted books."

"The second question is that where should the mutation happen inside this.state? The mutation should happen in this.state.books."

"The third question is that how should the mutation happen? Are we going to delete something or add any new element or restructure existing elements? In this case, we want to sort the elements as per some criterion."

"The Update addon accepts two parameters. The first parameter is the object that we want to mutate. The second parameter tells us where and how should the mutation take place in the first parameter. In this case, we want to mutate this.state. In this.state, we want to update the books with the sorted books. Therefore, our code will look similar to the following:"

Update(this.state, { books: { sortedBooks }})

Available commands

The Update addon provides different commands to perform the mutations in arrays and objects. The syntax of these commands is inspired by MongoDB's query language.

"Most of these commands operate on array objects allowing us to push in an array or unshift an element from the array. It also supports replacing the target entirely using the set command. Other than this, it also provides the merge command to merge new keys with an existing object." Mike explained.

"Shawn, my favorite command provided by this addon is apply. It takes a function and applies that function to the current value of the target that we want to modify. It gives more flexibility to make changes in the complex data structures. It's also a hint for your next task. Try to use it to sort the books by title without mutating." Mike challenged.

"Sure. Here you go," said Shawn.

    // src/App.js

import Update from 'react-addons-update';
  
_sortByTitle() {
  let sortByAttribute = this.state.sorting === 'asc' ? "title" : "-title";
  console.log("Before sorting");
  console.log(this.state.books[0].title);
  let newState = Update(this.state,
                        { books: { $apply: (books) => { return books.sort(sortBy(sortByAttribute)) } },
                          sorting: { $apply: (sorting) => { return sorting === 'asc' ? 'desc' : 'asc' } } });
  console.log("After sorting");
  console.log(this.state.books[0].title);
  this.setState(newState);
  }
Available commands

"Perfect, Shawn. Using the Update addon makes it very easy to manage complex state without actually mutating it."

Tip

Check https://facebook.github.io/immutable-js/docs/ for a complete solution for using immutable data structures in JavaScript.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.210.96