Create React components in a way analogous to React.createClass
, but powered by a subset of the stampit API.
This library has been replaced by react-stamp.
react-stampit can be installed via npm
npm install react-stampit
or by downloading the latest release.
This library is the result of wondering about what other ways a React component could be represented. Stamps are a cool concept, and more importantly have proven to be a great alternative to React.createClass
and the ES2015 class
due to their flexibility and use of multiple kinds of prototypal inheritance.
react-stampit has an API similar to React.createClass
. The factory accepts two parameters, the React library and a description object.
stampit(React, {
init: [],
state: {},
statics: {},
// convenience props for React statics
contextTypes: {},
childContextTypes: {}.
propTypes: {},
defaultProps: {},
// ...methods
});
The best part about stamps is their composability. What this means is that n
number of stamps can be combined into a new stamp which inherits each passed stamp's behavior. This is perfect for React, since class
is being pushed as the new norm and does not provide an idiomatic way to use mixins. (classical inheritance 😞). While stamp composability on the surface is not idiomatic, the conventions used underneath are; it is these conventions that provide a limitless way to extend any React component.
mixin1.jsx
export default {
componentWillMount() {
this.state.mixin1 = true;
},
};
mixin2.jsx
export default {
componentWillMount() {
this.state.mixin2 = true;
},
};
component.jsx
import stampit from 'react-stampit';
import * as cache from 'react-stampit/utils/cache';
const id = cache.uniqueId();
export default React => {
return cache.find(id) || cache.save(
stampit(React, {
state: {
comp: false,
mixin1: false,
mixin2: false,
},
_onClick() {
return this.state;
},
componentWillMount() {
this.state.comp = true;
},
render() {
return <input type='button' onClick={() => this._onClick()} />;
},
}), id
);
};
app.jsx
import React from 'react';
import componentFactory from './component';
import mixin1 from './mixin1';
import mixin2 from './mixin2';
const Component = componentFactory(React).compose(mixin1, mixin2);
/**
* Do things
*/
shallowRenderer.render(<Component />);
const button = shallowRenderer.getRenderOutput();
assert.deepEqual(
button.props.onClick(), { comp: true, mixin1: true, mixin2: true },
'should return component state with all truthy props'
);
>> ok
You may have noticed several interesting behaviors.
- component is a factory
This design pattern is optional, but recommended. Component factories are react-stampit's solution for avoiding the often hard to debug problems created by multiple instances of React. Read more about that here. By injecting the React library, we are able to guarantee the version and instance of React that a component will receive.
- caching
This goes hand in hand with designing components as factories. Node.js's internal caching will not work as expected for component factories, react-stampit's cache utility can be used as a replacement.
- no autobinding
Event handlers require explicit binding. No magic. This can be done using .bind
or through lexical binding with ES2015 arrow functions as shown in the example.
- no
call super
React methods are wrapped during composition, providing functional inheritance. Sweet.
- mixins are POJOs
This is shorthand syntax for:
stampit(null, {
// stuff
});
If you feel limited by class
, or want a fresh take on React.createClass
, maybe give react-stampit a try and learn more about what stampit is all about. And please report any issues you encounter!