Learn React.js a Little at a Time, Part 3

After the first two parts of this series, you have a simple app for posting messages into a feed. Today, we are going to restructure our app to use the Flux architecture. This is a common way to build React apps and helps to keep everything simple.

What is Flux?

The Flux architecture is a set of principles for structuring your React app. Unidirectional data flow is its defining characteristic. User actions flow from the interface to a data store. This data store then determines what data needs to change based on this action. Once the data changes, views are notified and re-rendered with the new data.

Vocabulary

Actions

Actions are the set of interactions with your app. Many will come from how the user works with your app. Other sources can produce actions, too. For instance, the server can send an update or a timer can trigger one.

Dispatcher

The dispatcher is a pub-sub bus. It emits actions to the stores that listen for them.

Store

A place where your data lives. Responsible for receiving actions and changing the state of the application based on these actions. Once the state is updated, it alerts any subscribed views of the change.

View

A React component that displays data from a store. The component should not change the application state. Instead, the view creates actions for a store to handle.

Implementing Flux

First, we’ll want to install a couple dependencies that we need for this.npm install --save-dev flux immutableThe Flux library is Facebook’s implementation of the Flux architecture. There are over a dozen implementations. In fact, I suggest you look into a few before deciding on one. Redux is a popular choice worthy of consideration.

Immutable.js

The other library we add is Immutable. Immutable is a library of immutable data structures built by Facebook.What are immutable data structures? They are data structures which cannot be modified. Operations like adding or removing values on them create new data structures instead.This seems like it would be inefficient and consume a lot of memory. But! The data structures themselves share elements in memory. Because they don’t copy everything, each operation is efficient.What do we gain over using plain old JavaScript objects and arrays? Immutability eliminates many hard to debug problems where data is being changed in many different places. Also, it makes detecting changes instant. This ends up being one of the best ways to increase React app performance.Our initial change is to create a file with constants at app/constants/message-constants.js. This is a convention so we have common values all in one place. In this case we define the action types.export const CREATE = 'CREATE_MESSAGE';
Next up is to define our action creator. This is the class responsible for sending actions to the dispatcher. Create a file at app/actions/message-actions.js with the following.import { CREATE } from '../constants/message-constants';
export default class MessageActions {  
 constructor(dispatcher) {    
   this.dispatcher = dispatcher;  
  }  
  create(newContent) {    
    this.dispatcher.dispatch({      
      type: CREATE,      
      content: newContent    
    });  
  }
}
The definition at this point is simple. In the constructor we take a Dispatcher that we will use for emitting events. We also have one action, create. Setting up actions in this manner decouples the actions from stores.Finally, we need to define the store that handles these actions. Enter the following in app/stores/message-store.js.import AppDispatcher from '../dispatcher/app-dispatcher';
import { CREATE } from '../constants/message-constants';
import { ReduceStore } from 'flux/utils';
import Immutable from 'immutable';

export default class MessageStore extends ReduceStore {  
  getInitialState() {    
    return Immutable.fromJS({      
      messages: []    
    });  
  }  

  reduce(state, action) {    
    switch (action.type) {      
      case CREATE:        
        return state.update('messages', messages => messages.unshift({          
          id: messages.count(),          
          author: 'Zach Kuhn',          
          content: action.content,          
          createdAt: new Date()        
        }));      
      default:
        return state;    
    }  
  }
}
We are using Flux Util’s ReduceStore which treats each action as a reduce operation. Reduces are a functional concept where you “reduce” a collection to a value. To do this, you use a function that takes two arguments. The first is the accumulator, which is the result of the previous step in the reduction. The second is the current element in the collection. The function returns the result of your reduction.Check out how to do a sum using a reduce:var numbers = [1, 2, 3];
numbers.reduce(function (sum, current) {  
 return sum + current;}, 0);
//=> 6
The ReduceStore takes the same concept. With the reduce function, you have two parameters. The first is your current state (the accumulator). The second is an action that represents the current element in the action stream. From this function you return what your new state should be.Finally, let’s see what is needed to change in the views. Within app/main.jsx it now looks like:import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Message from './components/message';
import MessageCreator from './components/message-creator';
import css from './main.css';
import MessageActions from './actions/message-actions';
import { Dispatcher } from 'flux';
import MessageStore from './stores/message-store';

class App extends Component {  
 constructor(props) {    
   super(props);    
   this.dispatcher = new Dispatcher();    
   this.store = new MessageStore(this.dispatcher);    
   this.actions = new MessageActions(this.dispatcher);    
   this.state = { data: this.store.getState() };  
 }  

 componentDidMount() {    
   this.listener = this.store.addListener(this.onStateChanged.bind(this));  
 }

 componentWillUnmount() {    
   this.listener.remove();  
 }  

 addMessage(newContent) {    
   this.actions.create(newContent);  
 }  

 onStateChanged() {    
   this.setState({ data: this.store.getState() });  
 }  

 render() {    
   return (      
<div></div>
<messagecreator create="{this.addMessage.bind(this)}">  { this.state.data.get('messages').map(message => {  return </messagecreator>
<message author="{message.author}"  content="{message.content}" createdat="" {message.createdat}="" key="{message.id}"></message>
       }) }      

  );  
}
}

ReactDOM.render(<app>, document.querySelector('#react-mount'));</app>
Of note here, we set up the Flux components within our constructor. This means creating our Dispatcher, Actions creator, and Store.We also set the state as the value from the store. Notice we don’t set the state to the Immutable.js data structure. This is because setState expects a JavaScript object and merges it with the previous state.Whenever the component is added to the page, we subscribe to updates from the store and update our state when it changes. This will force the component to re-render. Likewise, once the component is removed, we also remove the listener.The other necessary change is using our Action creator whenever the user creates a new message.With these changes, our UI component no longer works with data. It can send off actions to change the state. It can re-render when the state changes. But it doesn’t have any logic for changing the state.Run the following command and then check out http://localhost:8080 to see the app in action!$ webpack-dev-server — content-base app/

What’s Next

We now have our app converted to using Flux. No functional changes, but the app is now looking more like what you’d need for a real app.Next article will cover converting this app to React Native, an exciting and novel way to write native apps using Javascript.Unlike the hybrid approach used by libraries like Cordova, React Native uses real native components. This approach gives you the benefit of native level performance while still being able to leverage your existing Javascript skillset.

LEARN REACT SERIES

       
  1. Part 1: Introduction to React.js
  2.    
  3. Part 2: ES2015 and JSX; Intermediate components with props and state
  4.    
  5. Part 3: Flux architecture and Immutable.js
  6.    
  7. Part 4: React Native

Want to learn why I think React has a bright future? Check out our article comparing React against Angular and Ember.

Subscribe to the Smashing Boxes Blog today!

Smashing Boxes is a creative technology lab that partners with clients, taking an integrated approach to solving complex business problems. We fuse strategy, design, and engineering with a steady dose of entrepreneurial acumen and just the right amount of disruptive zeal. Simply put, no matter what we do, we strive to do it boldly. Let's talk today!

Related Posts

Smashing Boxes is Now Part of Unboxed Ventures

Smashing Boxes is now part of Unboxed Ventures. See what the next chapter of entrepreneurship means for us.

view post

The Foundation of Digital Transformation

Digital Transformation -- the pivotal process of using emerging or new technologies to modify or improve how your organization or product functions; improving your business processes, corporate culture, and customer experiences to align with evolving market dynamics.

view post