3

I love how easy it is to do isomorphic JavaScript in React. The only thing that bothers me with how it works is that the data has to be downloaded twice by the browser. First in the DOM markup and again in JSON format to initialize state at runtime. All the data is already there in the DOM shouldn't React be able to rehydrate based on that alone?

screenshot of react component rendered server-side screenshot of the data duplicated in JSON format

I've done some experimentation doing progressive enhancement in KnockoutJS with a custom binding handler. I am hoping there is a way to so something similar with React.

//Custom binding to load the values into the view model from the DOM
ko.bindingHandlers.PE = {
    init: function(element, valueAccessor, allBindings) {
        var bindings = allBindings();
        if (typeof bindings.text === 'function') {
            bindings.text($(element).text());
        }
    }
};

Update

Putting logic into React that reads values from the DOM doesn't seem idiomatic. But nothing stops you from writing a little JavaScript to collect the values yourself! Given the example above you could do something like the following.

var comments = $('.media').map(function() {
  return {
    Author: $(this).find('.media-object').attr('src'),
    Text: $(this).find('.media-body').text()
  };
});

Then use that to initialize the react component client-side. I'm happy with that.

SavoryBytes
  • 35,571
  • 4
  • 52
  • 61

2 Answers2

2

Rendering is a one-way transform in React. It does not store enough information in the DOM to reconstruct the data structures that created the DOM. The data-react-id attributes are used for React's diffing algorithm but not for storing information about user data.

From your example (data-react-id's here are arbitrary, I wrote them by hand):

var Comment = React.createClass({
  render: function() {
    return (
      <div className="comment">
        <h3 className="commentAuthor">{this.props.comment.Author}</h3>
        {this.props.comment.Text}
      </div>
    );
  }
});

React.render(
  <Comment comment={{"Author": "Shellie", "Text": "semper, dui lectus"}} />,
  ...
);

...

<!-- Output -->
<div class="comment" data-react-id=".x.0">
  <h3 class="commentAuthor" data-react-id=".x.0.0">Shellie</h3>
  <span data-react-id=".x.0.1">semper, dui lectus</span>
</div>

There's no information in the DOM about where the string "Shellie" came from nor where "semper, dui lectus" came from. This example yields the same output:

var Comment = React.createClass({
  render: function() {
    return (
      <div className="comment">
        <h3 className="commentAuthor">Shellie</h3>
        semper, dui lectus
      </div>
    );
  }
});

React.render(
  <Comment />,
  ...
);

...

<!-- Output -->
<div class="comment" data-react-id=".x.0">
  <h3 class="commentAuthor" data-react-id=".x.0.0">Shellie</h3>
  <span data-react-id=".x.0.1">semper, dui lectus</span>
</div>
Ross Allen
  • 43,772
  • 14
  • 97
  • 95
0

What you really want to do is store the server side data you used to render in a javascript variable via a script tag, then initialize your react from that client side object rather than from the server.

  • Right, that's what the screenshot above is doing, and it works. But I think it would be useful if there was some kind of annotation that would allow React to use the values from the DOM to initialize its runtime model that could then be updated with additional data over time. – SavoryBytes Feb 17 '15 at 19:07
  • Yeah as the other commenter says, that's against the one way data flow idea. Sorry, no ideas of a better way to do it really, should have read more thoroughly –  Feb 17 '15 at 19:40