Skip to content

Instantly share code, notes, and snippets.

@ericclemmons
Last active August 29, 2015 14:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericclemmons/70b5c698f37689ce4d51 to your computer and use it in GitHub Desktop.
Save ericclemmons/70b5c698f37689ce4d51 to your computer and use it in GitHub Desktop.
Flummox FluxContainer that runs actions, watches stores, and renders a component.
import assign from "object-assign";
import Flux from "flummox";
import React from "react";
export default React.createClass({
displayName: "FluxContainer",
contextTypes: {
flux: React.PropTypes.instanceOf(Flux),
},
componentDidMount() {
this.runActions(this.props.actions);
},
componentWillMount() {
this.listenToStores(this.props.stores);
},
componentWillUnmount() {
this.unlistenToStores(this.props.stores);
},
getDefaultProps() {
return {
actions: {},
stores: {},
};
},
getInitialState() {
const state = {};
for (let name in this.props.stores) {
const store = this.context.flux.getStore(name);
const getStoreState = this.props.stores[name];
assign(state, getStoreState(store));
}
// State is only set when all values exist
for (let key in state) {
if (state[key] === undefined) {
return null;
}
}
return state;
},
listenToStores(stores) {
for (let name in stores) {
this.context.flux.getStore(name).addListener("change", this.updateState);
}
},
render() {
if (!this.state) {
return false;
}
return (
<this.props.component {...this.state} />
);
},
runActions(actions) {
for (let name in actions) {
const action = actions[name];
action(this.context.flux.getActions(name));
}
},
unlistenToStores(stores) {
Object.keys(stores).forEach((name) => {
this.context.flux.getStore(name).removeListener("change", this.updateState);
});
},
updateState() {
this.setState(this.getInitialState());
}
});
import React from "react";
import FluxContainer from "./FluxContainer";
import User from "./User";
export default React.createClass({
displayName: "UserContainer",
render() {
return (
<FluxContainer
actions={{
user: (actions) => actions.fetch(this.props.id)
}}
stores={{
user: (store) => ({
user: store.find(this.props.id)
})
}}
component={User}
/>
);
}
});
import React from "react";
import Router from "react-router";
import UserContainer from "../components/UserContainer";
const { State } = Router;
export default React.createClass({
displayName: "UserHandler",
mixins: [ State ],
render() {
return (
<UserContainer id={this.getParams().id} />
);
}
});
@ericclemmons
Copy link
Author

This is similar, but slightly different, from <FluxComponent /> so that the following can be achieved:

  • <User /> component requires this.props.user, so element creation is delayed until everything's loaded. With <FluxComponent />, the element is rendered to the view, so the <User /> component fails unless it treats this.props.user as optional.
  • <UserHandler /> is the route handler that decouples the router params from the <UserContainer /> by passing along the id.
  • <UserContainer /> handles all data fetching from the initial fetch request to the find lookup.

Optionally (not in this gist), <FluxContainer /> can show a Loading message.

@RookieOne
Copy link

Love User Handler idea. Reminds me of Ember.Route

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment