React.js 2-way bindings: two-levels deep path in valueLink
Edit:
I realized that deep-path for LinkedState
is pretty cool so I try to implement it.
The code: https://gist.github.com/tungd/8367229
Usage: http://jsfiddle.net/uHm6k/3/
As the document stated, LinkedState
is a wrapper around onChange/setState
and meant for simple case. You can always write the full onChange/setState
to achieve what you want. If you really want to stick with LinkedState
, you can use the non mixin version, for example:
getInitialState: function() { return { values: [ { type: "translateX", x: 10 }, { type: "scaleX", x: 1.2 } ]}},handleTypeChange: function(i, value) { this.state.values[i].type = value this.setState({ values: this.state.values })},render: function() { ... this.state.values.map(function(item, i) { var typeLink = { value: this.state.values[i].type, requestChange: this.handleTypeChange.bind(null, i) } return <div><input valueLink={typeLink}/></div> }, this) ...}
Here is working JSFiddle: http://jsfiddle.net/srbGL/
You can implement your own mixin if the base mixin doesn't satisfy you.
See how this mixin is implemented:
var LinkedStateMixin = { /** * Create a ReactLink that's linked to part of this component's state. The * ReactLink will have the current value of this.state[key] and will call * setState() when a change is requested. * * @param {string} key state key to update. Note: you may want to use keyOf() * if you're using Google Closure Compiler advanced mode. * @return {ReactLink} ReactLink instance linking to the state. */ linkState: function(key) { return new ReactLink( this.state[key], ReactStateSetters.createStateKeySetter(this, key) ); }};/** * @param {*} value current value of the link * @param {function} requestChange callback to request a change */function ReactLink(value, requestChange) { this.value = value; this.requestChange = requestChange;}
https://github.com/facebook/react/blob/fc73bf0a0abf739a9a8e6b1a5197dab113e76f27/src/addons/link/LinkedStateMixin.jshttps://github.com/facebook/react/blob/fc73bf0a0abf739a9a8e6b1a5197dab113e76f27/src/addons/link/ReactLink.js
So you can easily try to write your own linkState
function based on the above.
linkState: function(key,key2) { return new ReactLink( this.state[key][key2], function(newValue) { this.state[key][key2] = newValue; } );}
Notice that I didn't use the ReactStateSetters.createStateKeySetter(this, key)
.https://github.com/facebook/react/blob/fc73bf0a0abf739a9a8e6b1a5197dab113e76f27/src/core/ReactStateSetters.jsBy looking at the source code again you can find out this method doesn't do so much except it creates a function and does little caching optimizations:
function createStateKeySetter(component, key) { // Partial state is allocated outside of the function closure so it can be // reused with every call, avoiding memory allocation when this function // is called. var partialState = {}; return function stateKeySetter(value) { partialState[key] = value; component.setState(partialState); };}
So you should definitely try to write your own mixin.This can be very useful if you have in your state a complex object and you want to modify it through the object API.
I do it without using value-link addon.
Here is a demo: http://wingspan.github.io/wingspan-forms/examples/form-twins/
The secret sauce is to only define one onChange function:
onChange: function (path, /* more paths,*/ value) { // clone the prior state // traverse the tree by the paths and assign the value this.setState(nextState);}
use it like this:
<input value={this.state['forms']['0']['firstName']} onChange={_.partial(this.onChange, 'forms', '0', 'firstName')} />
If you have many (value, onChange)
pairs that you have to pass around everywhere, it might make sense to define an abstraction around this similar to ReactLink, but I personally got pretty far without using ReactLink.
My colleagues and I recently open sourced wingspan-forms, a React library that helps with with deeply nested state. We leverage this approach heavily. You can see more example demos with linked state on the github page.