Component Event Bubbling in React

Part One of Data Flow with Facebook's React.js Library

React is an open source JavaScript library from Facebook and Instagram for building user interfaces (UI). React powers the UI of Instagram's web client and Facebook's Ad Management products. There are a number of benefits, ranging from nice templates with JSX, to a fast, virtual DOM. React's site does a great job covering the benefits in more depth and provides some easy-to-follow tutorials.

A challenge in getting started with React in your own applications, though, is understanding how user interactions with components bubble up to affect your data and, in turn, redraw your React UI. This is by design: React is model/controller agnostic. You can pair it with plain old JavaScript, or as the view component of an MVC framework (like Backbone.js). While powerful, this flexibility leaves you to responsibly closing the interaction feedback loop.

Facebook's coverage of Flux, their client-side architecture featuring React, offers a glimpse at the structural decisions they've made on the model and controller fronts. Flux is heavy duty, in the sense that it's robust enough for Facebook to build their entire UI for advertising support around it. Following along with Flux's Todo List tutorial, you'll have the opposite problem as with React's standalone version: there's enough indirection some of the key concepts are hard to see.

In a series of three posts, I'll focusing on three important questions involving data flow in React applications (inspired by the Flux architecture):

  1. How do DOM events bubble up and become component events?
  2. How do component events affect state?
  3. How do components know when state has changed?

How do DOM events bubble up and become component events?

Short answer: components that capture DOM events tend to expose "component event" handlers as props.

Imagine the Item Adder of a todo application. For simplicity, let's say the DOM event it listens for is a form submit:

/** @jsx React.DOM */
var TodoAdder = React.createClass({
 
  propTypes: {
    onAdd: React.PropTypes.func.isRequired,
  }, 
  
  render: function() {
    return (
    <form onSubmit={this._handleSubmit}>
      <input name="todo" />
    </form>
    );   
  },
  
  _handleSubmit: function(e) {
    e.preventDefault();
    this.props.onAdd($(e.target).find("input[name=todo]").val());  
  }  
 
});

A few interesting things are happening in this simple component:

  1. We're defining the component's prop signature, or API, via the `propTypes` property. Higher-level components that are using this TodoAdder component know they must register an `onAdd` handler via a prop.
  2. In the JSX, we're registering a DOM event handler on form submit.
  3. The submit handler is where the DOM event is transformed into a component event that bubbles up to its parent component's `onAdd` handler. The value bubbled up to `onAdd` is just the todo input's value.

What is the value of this transformation? It cleanly encapsulates the UI of the 'Adder' component by way of a contract. So long as the props contract does not change, the Adder is free to improve its implementation. Composition is improved. All the parent component needs to worry about is handling `onAdd`:

/** @jsx React.DOM */
var TodoApp = React.createClass({

   render: function() {
    return (
    <div>
      <h1>Todos</h1>
      <TodoAdder onAdd={this._handleAdd} />  
    </div>  
    );
  },
 
  _handleAdd: function(todo) {
    // todo
  }
 
}); 

Being able to compose user interfaces with well encapsulated component events is powerful in how well it scales to complex UIs. The hardest part is deciding where components should start and end and what their props contracts should be.

Comments

Robin's avatar
Robin
The HTML tag is very useful for me. I will try this more time . Thanks for share.
clippingpatharea's avatar
clippingpatharea
Absolutely beautiful works.. This is what called “Creativity”.
Andy's avatar
Andy
good article, when I can read your post 3 for How do components know when state has changed?

Leave a comment