Skip to content

Instantly share code, notes, and snippets.

@ericclemmons
Last active August 29, 2015 14:17
Show Gist options
  • Select an option

  • Save ericclemmons/70b5c698f37689ce4d51 to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown
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
Copy Markdown

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