Question

My state is:

[
  {type: "translateX", x: 10},
  {type: "scaleX", x: 1.2}
]

I’m using Two-Way Binding Helpers and I can’t provide a valid key string for linkState:

this.state.map(function(item, i) {
  return <div><input valueLink={this.linkState( ??? )}></div>
}

Would be nice if this.linkState accepted some query syntax, such as "0.type" to retrieve "translateX" from my example.

Are there any workarounds?


I wrote DeepLinkState mixin which is a drop-in replacement for React.addons.LinkedStateMixin. Usage example:

this.state.map(function(item, i) {
  return <div><input valueLink={this.linkState([i, "x"])}></div>
}

linkState("0.x") is also acceptable syntax.

Was it helpful?

Solution

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/

OTHER TIPS

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.js https://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.js By 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.

I wrote a blogpost about it: http://blog.sendsonar.com/2015/08/04/angular-like-deep-path-data-bindings-in-react/

But basically I created a new component that would accept the 'state' of parent and a deep path, so you don't have to write extra code.

<MagicInput binding={[this, 'account.owner.email']} />

There's a JSFiddle too so you can play with it

Here's the tutorial explaining how to handle things like this.

State and Forms in React, Part 3: Handling the Complex State

TL;DR:

0) Don't use standard links. Use these.

1) Change your state to look like this:

collection : [
  {type: "translateX", x: 10},
  {type: "scaleX", x: 1.2}
]

2) Take link to the collection:

var collectionLink = Link.state( this, 'collection' );

3) Iterate through the links to its elements:

collectionLink.map(function( itemLink, i ) {
  return <div><input valueLink={itemLink}></div>
})

I took a different approach which does not employ mixins and does not automatically mutate the state

See github.com/mcmlxxxviii/react-value-link

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top