Mixins Are Dead. Long Live Composition

Dan Abramov
Mar 13, 2015 · 6 min read

Utility Functions

This case is a no-brainer. If you use mixins to share utility functions, extract them to modules and import and use them directly.

Lifecycle Hooks and State Providers

This is the main use case for mixins. If you’re not very familiar with React’s mixin system, it tries to be smart and “merges” lifecycle hooks. If both the component and the several mixins it uses define the componentDidMount lifecycle hook, React will intelligently merge them so that each method will be called. Similarly, several mixins can contribute to the getInitialState result.

  • As you use more mixins in a single component, they begin to clash. For example, if you use something like StoreMixin(SomeStore) and you add another StoreMixin(OtherStore), React will throw an exception because your component now has two versions of methods with the same names. Different mixins will also clash if they define the same state fields.
  • Mixins tend to add more state to your component whereas you should strive for less. You should read the excellent Why Flux Component is better than Flux Mixin essay by Andrew Clark on this topic.
  • Mixins complicate performance optimizations. If you define the shouldComponentUpdate method in your components (manually or via PureRenderMixin), you might have issues if some of the mixins need their own shouldComponentUpdate implementations to be taken into account. This can be solved by adding even more “merging” magic, but is it really the way forward?

Enter Higher-Order Components

I first learned about this pattern from a gist by Sebastian Markbåge. The gist is a little bit cryptic if you’re not yet fully comfortable with ES6 syntax, so I’m going to use the “Flux Store mixin” example to explain it.

function StoreMixin(...stores) {
var Mixin = {
getInitialState() {
return this.getStateFromStores(this.props);
},
componentDidMount() {
stores.forEach(store =>
store.addChangeListener(this.handleStoresChanged)
);
this.setState(this.getStateFromStores(this.props));
},
componentWillUnmount() {
stores.forEach(store =>
store.removeChangeListener(this.handleStoresChanged)
);
},
handleStoresChanged() {
if (this.isMounted()) {
this.setState(this.getStateFromStores(this.props));
}
}
};
return Mixin;
}
var UserProfilePage = React.createClass({
mixins: [StoreMixin(UserStore)],
propTypes: {
userId: PropTypes.number.isRequired
},
getStateFromStores(props) {
return {
user: UserStore.get(props.userId);
}
}
render() {
var { user } = this.state;
return <div>{user ? user.name : 'Loading'}</div>;
}
function connectToStores(Component, stores, getStateFromStores) {
const StoreConnection = React.createClass({
getInitialState() {
return getStateFromStores(this.props);
},
componentDidMount() {
stores.forEach(store =>
store.addChangeListener(this.handleStoresChanged)
);
},
componentWillUnmount() {
stores.forEach(store =>
store.removeChangeListener(this.handleStoresChanged)
);
},
handleStoresChanged() {
if (this.isMounted()) {
this.setState(getStateFromStores(this.props));
}
},
render() {
return <Component {...this.props} {...this.state} />;
}
});
return StoreConnection;
};
var ProfilePage = React.createClass({
propTypes: {
userId: PropTypes.number.isRequired,
user: PropTypes.object // note that user is now a prop
},
render() {
var { user } = this.props; // get user from props
return <div>{user ? user.name : 'Loading'}</div>;
}
});
// Now wrap ProfilePage using a higher-order component:ProfilePage = connectToStores(ProfilePage, [UserStore], props => ({
user: UserStore.get(props.userId)
});

What’s Next

I plan to use higher-order components in the next version of React DnD.

Other Approaches

There are other perfectly valid patterns for composition, such as composition right inside render() as used in Flummox. It is also based on nesting, but is less verbose than the higher-order components. This will be even easier in React 0.14, as it switches to parent-based context.

    Dan Abramov

    Written by

    Working on @reactjs. Co-author of Redux and Create React App. Building tools for humans.

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade