acdlite/flummox

View on GitHub
docs/dist/flummox/data/docs/guides/why-flux-component-is-better-than-flux-mixin.json

Summary

Maintainability
Test Coverage
{"path":"guides/why-flux-component-is-better-than-flux-mixin","content":"Why FluxComponent > fluxMixin\n=============================\n\nIn the [React integration guide](react-integration), I suggest that using [FluxComponent](/flummox/docs/api/fluxcomponent) is better than using [fluxMixin](/flummox/docs/api/fluxmixin), 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.\n\nMy 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 \"React Way\":\n\n- Declarative > imperative\n- Composition > inheritance\n- State is evil\n\nDeclarative > imperative\n------------------------\n\nThis 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.\n\nYou 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.\n\n\nComposition > inheritance\n-------------------------\n\nReact 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.\n\nComponents 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 *from the owner*.\n\nHowever, 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.\n\nFor instance, here's a component that renders a single blog post, based on the id of the post.\n\n```js\nconst BlogPost = React.createClass({\n  mixins: [fluxMixin({\n    posts: (store, props) => ({\n      post: store.getPost(props.id),\n    })\n  })],\n\n  render() {\n    <article>\n      <h1>{this.state.post.title}</h1>\n      <div>{this.state.post.content}</div>\n    </article>\n  }\n});\n```\n\nCan 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.\n\nConsider that the owner component (BlogRoll, let's say) has to pass down an `id` 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 *and* each of its children getting data from the store, each with their own event listeners, and each calling `setState()` every time the store changes. D'oh!\n\nA better approach is to separate the data fetching logic from the logic of rendering the post. Instead of having a prop `id`, BlogPost should have a prop `post`. It shouldn't concern itself with how the data is retrieved — that's the concern of the owner.\n\nAfter we rewrite BlogPost, it looks something like this:\n\n```js\nconst BlogPost = React.createClass({\n  render() {\n    <article>\n      <h1>{this.props.post.title}</h1>\n      <div>{this.props.post.content}</div>\n    </article>\n  }\n});\n```\n\nAnd its owner looks something like this:\n\n```js\nconst BlogPostPage = React.createClass({\n  mixins: [fluxMixin({\n    posts: (store, props) => ({\n      post: store.getPost(props.id),\n    })\n  })],\n\n  render() {\n    <div>\n      <SiteNavigation />\n      <MainContentArea>\n        <BlogPost post={this.state.post} />\n      </MainContentArea>\n      <SiteSidebar />\n      <SiteFooter />\n    </div>\n  }\n})\n```\n\n*For the sake of this example, let's just assume the `id` prop magically exists and is derived from the URL. In reality, we'd use something like React Router's [State mixin]( https://github.com/rackt/react-router/blob/master/docs/api/mixins/State.md).*\n\nThere's another problem, though. Every time the store changes, fluxMixin calls `setState()` on BlogPostPage, triggering a re-render of the *entire* component.\n\nWhich brings us to the final point...\n\nState is evil\n-------------\n\nOnce you've grokked the basics, this is perhaps the most important thing to know about React. To [think in React](http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html) 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.\n\nOn 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 `posts` 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.\n\nAlright, 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:\n\n```js\nconst BlogPostWrapper = React.createClass({\n  mixins: [fluxMixin({\n    posts: (store, props) => ({\n      post: store.getPost(props.id),\n    })\n  ]\n\n  render() {\n    <BlogPost post={this.state.post} />\n  }\n});\n```\n\nThis works. But it's kind of tedious, right? Imagine creating a wrapper like this for every single component that requires data from a store.\n\nWouldn'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?\n\nYep! It's called FluxComponent.\n\n```js\nclass BlogPostPage extends React.Component {\n  render() {\n    <div>\n      <SiteNavigation />\n      <MainContentArea>\n        <FluxComponent connectToStores={{\n          posts: store => ({\n            post: store.getPost(this.props.postId),\n          })\n        }}>\n          <BlogPost />\n        </FluxComponent>\n      </MainContentArea>\n      <SiteSidebar />\n      <SiteFooter />\n    </div>\n  }\n}\n```\n\nThe state fetched by `connectToStores()` 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:\n\n```js\nclass BlogPostPage React.Component {\n  render() {\n    <div>\n      <SiteNavigation />\n      <MainContentArea>\n        <FluxComponent\n          connectToStores={{\n            posts: store => ({\n              post: store.getPost(this.props.postId),\n            })\n          }}\n          render={storeState => {\n            // render whatever you want\n            return <BlogPost {...storeState} />;\n          }}\n        />\n      </MainContentArea>\n      <SiteSidebar />\n      <SiteFooter />\n    </div>\n  }\n}\n```\n\nDo what's right\n---------------\n\nIf 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, [React Tween State](https://github.com/chenglou/react-tween-state) is a great project that wouldn't make sense as a component.\n\nEither way, both fluxMixin and FluxComponent are available for you to use, and both are pretty great :)\n\nIf you have any suggestions for how they could be improved, please let me know by submitting an issue.\n"}