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.
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.
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
};
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.