statefy v1.0.5
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
Autocompletecomponent to be independent and happy. That is... You can let it keep it'sopenstate. In this case, theController-Componentwill not even know then theAutocompletecomponent changed from open to closed or vice-versa. TheAutocompletewill 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
openorclosedstate and I will do so. You tell me when the user clicked thetogglebutton and I will re-render you passing the appropriate value for theopenprop". If you go that route, for everypropyou 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, theAutocompletecomponent doesn't have to keep it's own state. TheController-Componentdoes.
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 statefyNow...
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.