Statefy
A ReactJS mixin that allows you to transparently handle props
and state
on ReactJS components.
Motivation
Let's assume we have a Textbox
ReactJS component that has some props
. Let's assume, also, that we have an external Controller-Component
that is responsible for
handling the view state and rendering. Controller-Component
renders the Textbox
component every time it needs the Textbox
to be different. This problem is very simple and
shouldn't get you into trouble.
Now let's assume you have a slightly more complex component called Autocomplete
. Autocomplete
has a button that, when clicked, toggles the state of a dropdown list.
Now you have 2 options and a dilemma:
- You can let the
Autocomplete
component to be independent and happy. That is... You can let it keep it'sopen
state. In this case, theController-Component
will not even know then theAutocomplete
component changed from open to closed or vice-versa. TheAutocomplete
will be master of it's own state and future re-renders won't have any effect. - You can say: "Look, Autocomplete, I don't want you to keep your own state. I want you to inform me every time you want to be rendered either in the
open
orclosed
state and I will do so. You tell me when the user clicked thetoggle
button and I will re-render you passing the appropriate value for theopen
prop
". If you go that route, for everyprop
you passed toAutocomplete
, you need a way to be informed that it should change, so you can re-render the component passing the new value. If used this way, theAutocomplete
component doesn't have to keep it's own state. TheController-Component
does.
The problem is: When you create and publish component, you don't know whether the developer using your component will want it to behave as (1) or as (2).
The solution
Statefy is a mixin that, when added to your ReactJS component, will make it to behave consistently regardless of whether the developer using it prefers (1) or (2).
Getting the value of a property
In order to get the value of a property, regardless of it being prop
or state
:
var open = this.get("open");
The get
function will first try to find a matching property on the state
. If it doesn't find it, it will try to take it from the props
.
Setting the value of a property
In order to set the value of a property, regardless of if being prop
or state
:
this.set("open", true);
When you change a property value, Statefy will trigger an event name dynamically generated according to the property name, for instance, if the property is called "open", statefy will trigger the "onOpenChange" event, so you could handle it like this:
<Autocomplete
id="myAutocomplete"
onOpenChange= {
function (e) {
// if value has changed
e.preventDefault();
var newState = React.addons.update(
this.state,
{autocompleteOpen: {value: {$set: e.value}}});
this.setState(newState);
}.bind(this)
}
/>
Once the event is handled, e.preventDefault
prevents the Autocomplete
to store the open
property as an internal
state. When you use e.preventDefault
, you are using the component in the approach (2), that is, you, the Controller-Component
,
are responsible for re-rendering the Aucomplete
component because the open
property changed.
If we didn't use the e.preventDefault
, the Autocomplete
would store open
as state and future renders would simply ignore
what would be passed as prop. This would be approach (1).
If the property was called value
, the event name generated would be simply onChange
. Example:
<Textbox
id="textbox2"
value={this.state.textbox2.value}
onChange={
function (e) {
e.preventDefault();
var newState = React.addons.update(
this.state,
{textbox2: {value: {$set: e.value}}});
this.setState(newState);
}.bind(this)
}
/>
You can even assign an event handler to deal with any property change, the event is onAnyStateChanged
. Example:
<Textbox
id="textbox1"
value={this.state.textbox1.value}
onAnyChange={
function (e) {
// if value has changed
if (e.key == 'value') {
e.preventDefault();
var newState = React.addons.update(
this.state,
{textbox1: {value: {$set: e.value}}});
this.setState(newState);
}
}.bind(this)
}
/>
Installation
If you are using CommonJS:
Install by npm
:
npm install statefy
Now...
var statefy = require("statefy");
const Textbox = React.createClass({
mixins: [statefy]
});
Or, alternatively, just download the mixin and use it as described above.
License
Statefy is provided under the MIT license.