React at Preact

Smart Components

The distinction key deciding factor between smart and dumb components is whether they need to access external state.

Smart components are less reusable outside a single application or set of applications because they are aware of application-level state.

For instance, Smart components may bind to Flux actions or stores in order to directly integrate with business logic and react to changes. Whenever possible, you should favor creating dumb components and reduce the surface area of components which bind to application logic.

Rule of Thumb: Use Sparingly

If you find yourself creating smart components very often, think very carefully about whether the state should be managed at a higher level instead of within the component itself.

Many things which may initially feel like they should bind to state are better expressed as dumb components included within a page or a higher-level smart component.

Example - A Shopping Cart

As an example, you may have a page (/checkout) to allow a user to purchase the contents of their shopping cart. On that page, you will likely have some components to display the items in the cart, calculate sales tax and shipping, and show "other people also bought" recommendations.

Initially, it may seem like the shopping cart component should connect to a ShoppingCartStore to retrieve the list of items and render them each and include a button on each row to call ShoppingCartActions.changeQuantity or ShoppingCartActions.removeFromCart.

If you took this approach, you'd have business logic in ShoppingCart component, and you'd insert it into the CheckoutPage using JSX like this:

module.exports = React.createClass({
  // ... snip

  // THE WRONG WAY
  render() {
    return (
      <div>
        <h1>Your Cart</h1>
        <ShoppingCart />
        <ShippingInfo />
        <BuyNowButton />
      </div>
    );
  }

  // ... snip
};

This may feel very clean at the top, but pushes too much duplicate logic down into the children components. In this case, all three of the Smart components shown will need to be aware of application state and all will update application state directly via Flux actions.

Instead, in React land, you should instead create the ShoppingCart component as a dumb component, and pass the cart as props. You should also pass down a callback for the actions you want to take, so that you can mutate application state centrally on the CheckoutPage component.

Consider instead the following approach:

module.exports = React.createClass({
  // ... snip

  // THE RIGHT WAY
  render() {
    return (
      <div>
        <h1>Your Cart</h1>
        <ShoppingCart cart={ this.state.cart } onItemRemove={ this.removeCartItem } onItemChangeQty={ this.changeCartItemQty } />
        <ShippingInfo cart={ this.state.cart } address={ this.state.userShippingAddress }/>
        <BuyNowButton cart={ this.state.cart } onBuyClick={ this.cartPurchased } />
      </div>
    );
  }

  // ... snip
};

Smells

If you find yourself referencing components without any props like in the wrong example above, this should be considered a code smell and you should make extra sure that using smart components in that case is the right approach.

Facebook has some good ideas for when to use state in components on their Thinking in React documentation page.