acdlite/flummox

View on GitHub
docs/dist/flummox/docs/guides/why-flux-component-is-better-than-flux-mixin.md/index.html

Summary

Maintainability
Test Coverage
<!doctype html>
<html class="no-js" lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <title>Flummox | Minimal, isomorphic Flux</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    
      <link rel="stylesheet" href="/flummox/css/app.min.css">
    
  </head>
  <body>
    <div id="app"><div data-reactid=".22l08caes5c" data-react-checksum="-255470133"><div style="height:2.25rem;" class="View" data-reactid=".22l08caes5c.0"><div class="View" style="background-color:#2083E0;position:fixed;width:100%;z-index:9999;" data-reactid=".22l08caes5c.0.0"><div class="View" data-reactid=".22l08caes5c.0.0.0"><div class="View" data-reactid=".22l08caes5c.0.0.0.0"><a href="/" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.0.0.0">Flummox</a></div></div><div class="View View--justifyContentFlexEnd View--flexGrow" data-reactid=".22l08caes5c.0.0.1"><div class="View" data-reactid=".22l08caes5c.0.0.1.0"><a href="/flummox/docs/guides" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.0.0">Guides</a><div class="View View--flexDirectionColumn" style="display:none;background-color:#2083E0;position:absolute;right:0;top:100%;width:16em;text-align:right;" data-reactid=".22l08caes5c.0.0.1.0.1"><div class="View" data-reactid=".22l08caes5c.0.0.1.0.1.0"><a href="/flummox/docs/guides/quick-start" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.0.1.0.0">Quick Start</a></div><div class="View" data-reactid=".22l08caes5c.0.0.1.0.1.1"><a href="/flummox/docs/guides/react-integration" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.0.1.1.0">React Integration</a></div><div class="View" data-reactid=".22l08caes5c.0.0.1.0.1.2"><a href="/flummox/docs/guides/why-flux-component-is-better-than-flux-mixin" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.0.1.2.0">Why FluxComponent &gt; fluxMixin</a></div></div></div><div class="View" data-reactid=".22l08caes5c.0.0.1.1"><a href="/flummox/docs/api" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.1.0">API</a><div class="View View--flexDirectionColumn" style="display:none;background-color:#2083E0;position:absolute;right:0;top:100%;width:16em;text-align:right;" data-reactid=".22l08caes5c.0.0.1.1.1"><div class="View" data-reactid=".22l08caes5c.0.0.1.1.1.0"><a href="/flummox/docs/api/store" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.1.1.0.0">Store</a></div><div class="View" data-reactid=".22l08caes5c.0.0.1.1.1.1"><a href="/flummox/docs/api/flux" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.1.1.1.0">Flux</a></div><div class="View" data-reactid=".22l08caes5c.0.0.1.1.1.2"><a href="/flummox/docs/api/actions" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.1.1.2.0">Actions</a></div><div class="View" data-reactid=".22l08caes5c.0.0.1.1.1.3"><a href="/flummox/docs/api/fluxcomponent" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.1.1.3.0">FluxComponent</a></div><div class="View" data-reactid=".22l08caes5c.0.0.1.1.1.4"><a href="/flummox/docs/api/higher-order-component" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.1.1.4.0">Higher-order component</a></div><div class="View" data-reactid=".22l08caes5c.0.0.1.1.1.5"><a href="/flummox/docs/api/fluxmixin" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.1.1.5.0">fluxMixin</a></div></div></div><div class="View" data-reactid=".22l08caes5c.0.0.1.2"><a href="https://github.com/acdlite/flummox" style="padding:0.375rem 0.75rem;color:#fff;width:100%;text-decoration:none;border:inherit solid 1px;border-width:;border-style:;" data-reactid=".22l08caes5c.0.0.1.2.0">GitHub</a></div></div></div></div><span data-reactid=".22l08caes5c.1"><div data-reactid=".22l08caes5c.1.$=1$/flummox/docs/guides/why-flux-component-is-better-than-flux-mixin"><article class="Doc" style="padding:3rem 0;" data-reactid=".22l08caes5c.1.$=1$/flummox/docs/guides/why-flux-component-is-better-than-flux-mixin.0"><div class="Container" data-reactid=".22l08caes5c.1.$=1$/flummox/docs/guides/why-flux-component-is-better-than-flux-mixin.0.0"><div class="Doc-content" data-reactid=".22l08caes5c.1.$=1$/flummox/docs/guides/why-flux-component-is-better-than-flux-mixin.0.0.0"><h1>Why FluxComponent &gt; fluxMixin</h1>
<p>In the <a href="react-integration">React integration guide</a>, I suggest that using <a href="/flummox/docs/api/fluxcomponent">FluxComponent</a> is better than using <a href="/flummox/docs/api/fluxmixin">fluxMixin</a>, even though they do essentially the same thing. A few people have told me they like the mixin form more, so allow me to explain.</p>
<p>My argument can be broken down into three basic points. Note that these aren’t my original ideas, nor are they unique to Flummox — they are the &quot;React Way&quot;:</p>
<ul>
<li>Declarative &gt; imperative</li>
<li>Composition &gt; inheritance</li>
<li>State is evil</li>
</ul>
<h2>Declarative &gt; imperative</h2>
<p>This is a no brainer. Remember the days before React, when you had to write one piece of code to render your application and another to update it? HAHAHAHA. That was awful. React showed us that expressing our views declaratively leads to clearer, more predictable, and less error-prone applications.</p>
<p>You might feel like fluxMixin and FluxComponent are equally declarative. They do have similar interfaces: a single argument/prop, that does (almost) the same thing. Still, as nice as fluxMixin’s interface is, there’s no beating a component in terms of clarity. A good rule of thumb in React is that everything that can be expressed as a component, should be.</p>
<h2>Composition &gt; inheritance</h2>
<p>React has a very clear opinion on composition vs. inheritance: composition wins. Pretty much everything awesome about React — components, unidirectional data flow, the reconciliation process — derives from the fact that a React app is just a giant tree of components composed of other components.</p>
<p>Components make your code easy to reason about. If you stick to the basics of using components and props in your React app, you don’t have to guess where data is coming from. The answer is always <em>from the owner</em>.</p>
<p>However, when you use fluxMixin, you’re introducing data into your component that comes not from the owner, but from an external source — your stores. (This is also true of FluxComponent, but to a lesser extent, as we’ll see later.) This can easily lead to trouble.</p>
<p>For instance, here’s a component that renders a single blog post, based on the id of the post.</p>
<pre><code class="language-js">const BlogPost = React.createClass({
  mixins: [fluxMixin({
    posts: (store, props) =&gt; ({
      post: store.getPost(props.id),
    })
  })],

  render() {
    &lt;article&gt;
      &lt;h1&gt;{this.state.post.title}&lt;/h1&gt;
      &lt;div&gt;{this.state.post.content}&lt;/div&gt;
    &lt;/article&gt;
  }
});
</code></pre>
<p>Can you spot the problem? What happens when you want to re-use this same component to display a list of blog posts on your home page? Does it really make sense for each BlogPost component to separately retrieve its own post data from the store? Nope, not really.</p>
<p>Consider that the owner component (BlogRoll, let’s say) has to pass down an <code>id</code> prop in order for BlogPost to work properly. Where do you think BlogRoll is going to get that id from? The store, of course. Now you have BlogRoll <em>and</em> each of its children getting data from the store, each with their own event listeners, and each calling <code>setState()</code> every time the store changes. D’oh!</p>
<p>A better approach is to separate the data fetching logic from the logic of rendering the post. Instead of having a prop <code>id</code>, BlogPost should have a prop <code>post</code>. It shouldn’t concern itself with how the data is retrieved — that’s the concern of the owner.</p>
<p>After we rewrite BlogPost, it looks something like this:</p>
<pre><code class="language-js">const BlogPost = React.createClass({
  render() {
    &lt;article&gt;
      &lt;h1&gt;{this.props.post.title}&lt;/h1&gt;
      &lt;div&gt;{this.props.post.content}&lt;/div&gt;
    &lt;/article&gt;
  }
});
</code></pre>
<p>And its owner looks something like this:</p>
<pre><code class="language-js">const BlogPostPage = React.createClass({
  mixins: [fluxMixin({
    posts: (store, props) =&gt; ({
      post: store.getPost(props.id),
    })
  })],

  render() {
    &lt;div&gt;
      &lt;SiteNavigation /&gt;
      &lt;MainContentArea&gt;
        &lt;BlogPost post={this.state.post} /&gt;
      &lt;/MainContentArea&gt;
      &lt;SiteSidebar /&gt;
      &lt;SiteFooter /&gt;
    &lt;/div&gt;
  }
})
</code></pre>
<p><em>For the sake of this example, let’s just assume the <code>id</code> prop magically exists and is derived from the URL. In reality, we’d use something like React Router’s <a href="https://github.com/rackt/react-router/blob/master/docs/api/mixins/State.md">State mixin</a>.</em></p>
<p>There’s another problem, though. Every time the store changes, fluxMixin calls <code>setState()</code> on BlogPostPage, triggering a re-render of the <em>entire</em> component.</p>
<p>Which brings us to the final point…</p>
<h2>State is evil</h2>
<p>Once you’ve grokked the basics, this is perhaps the most important thing to know about React. To <a href="http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html">think in React</a> is to find the minimal amount of state necessary to represent your app, and calculate everything based on that. This is because state is unpredictable. Props are, for the most part, derived from other props and state, but state can be anything. The more state in your application, the harder it is to reason about it. As much as possible, state in React should be an implementation detail — a necessary evil, not a crutch.</p>
<p>On an even more practical level, every time the state of a component changes, the entire component sub-tree is re-rendered. In our example from the previous section, BlogPostPage updates every time the <code>posts</code> store changes — including SiteNavigation, SiteSidebar, and SiteFooter, which don’t need to re-render. Only BlogPost does. Imagine if you’re listening to more than just one store. The problem is compounded.</p>
<p>Alright, so we need to refactor once again so that fluxMixin is only updating what needs to be updated. We already learned that we shouldn’t put the mixin inside BlogPost itself, because that makes the component less reusable. Our remaining option is to create a new component that wraps around BlogPost:</p>
<pre><code class="language-js">const BlogPostWrapper = React.createClass({
  mixins: [fluxMixin({
    posts: (store, props) =&gt; ({
      post: store.getPost(props.id),
    })
  ]

  render() {
    &lt;BlogPost post={this.state.post} /&gt;
  }
});
</code></pre>
<p>This works. But it’s kind of tedious, right? Imagine creating a wrapper like this for every single component that requires data from a store.</p>
<p>Wouldn’t it be great if there were a shortcut for this pattern — a convenient way to update specific parts of your app, without triggering unnecessary renders?</p>
<p>Yep! It’s called FluxComponent.</p>
<pre><code class="language-js">class BlogPostPage extends React.Component {
  render() {
    &lt;div&gt;
      &lt;SiteNavigation /&gt;
      &lt;MainContentArea&gt;
        &lt;FluxComponent connectToStores={{
          posts: store =&gt; ({
            post: store.getPost(this.props.postId),
          })
        }}&gt;
          &lt;BlogPost /&gt;
        &lt;/FluxComponent&gt;
      &lt;/MainContentArea&gt;
      &lt;SiteSidebar /&gt;
      &lt;SiteFooter /&gt;
    &lt;/div&gt;
  }
}
</code></pre>
<p>The state fetched by <code>connectToStores()</code> is transferred to the children of FluxComponent. If this auto-magic prop passing feels weird, or if you want direct control over rendering, you can pass a custom render function instead:</p>
<pre><code class="language-js">class BlogPostPage React.Component {
  render() {
    &lt;div&gt;
      &lt;SiteNavigation /&gt;
      &lt;MainContentArea&gt;
        &lt;FluxComponent
          connectToStores={{
            posts: store =&gt; ({
              post: store.getPost(this.props.postId),
            })
          }}
          render={storeState =&gt; {
            // render whatever you want
            return &lt;BlogPost {...storeState} /&gt;;
          }}
        /&gt;
      &lt;/MainContentArea&gt;
      &lt;SiteSidebar /&gt;
      &lt;SiteFooter /&gt;
    &lt;/div&gt;
  }
}
</code></pre>
<h2>Do what’s right</h2>
<p>If I’m leaving you unconvinced, just do what you feel is right. I think components are generally preferable to mixins, but as with any rule, there are exceptions. For instance, <a href="https://github.com/chenglou/react-tween-state">React Tween State</a> is a great project that wouldn’t make sense as a component.</p>
<p>Either way, both fluxMixin and FluxComponent are available for you to use, and both are pretty great :)</p>
<p>If you have any suggestions for how they could be improved, please let me know by submitting an issue.</p>
</div></div></article></div></span></div></div>

    
      <script src="/flummox/js/app.min.js" defer></script>
    
  </body>
</html>