Skip to content

ReactDOMComponentTree #5190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed

ReactDOMComponentTree #5190

wants to merge 10 commits into from

Conversation

sophiebits
Copy link
Collaborator

New module! With this, we have a new module that uses the component tree to store DOM nodes. Most of the logic in this file relates to markup adoption -- if we were to drop that (along with server rendering) this file could just be a return inst._nativeNode;.

This works with useCreateElement only because we need to reference each node as it's created.

Events is now the only thing using hierarchical IDs and ReactMount.getNode -- I'll use ReactDOMComponentTree.getInstanceFromNode to make that work.

it('should be able to switch between rendering null and a normal tag', () => {
spyOn(console, 'log');

it.only('should be able to switch between rendering null and a normal tag', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it.only

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, fixed.

@facebook-github-bot
Copy link

@spicyj updated the pull request.

@sophiebits sophiebits force-pushed the cotree branch 2 times, most recently from 569414c to 2188cf8 Compare October 16, 2015 04:55
@facebook-github-bot
Copy link

@spicyj updated the pull request.

@facebook-github-bot
Copy link

@spicyj updated the pull request.

@gaearon
Copy link
Collaborator

gaearon commented Oct 16, 2015

What is the big picture here? Getting rid of data-reactid eventually? Better resilience against evil browser plugins?

@sophiebits
Copy link
Collaborator Author

Getting rid of hierarchical IDs (replacing them with numbers, probably) and the giant hash map we have in ReactMount. We can probably kill data-reactid entirely when not using server-side rendering.

@jimfb
Copy link
Contributor

jimfb commented Oct 16, 2015

@spicyj My hope is that we can kill data-reactid entirely especially when using server-side rendering. Rehydrating a markup tree does not benefit from having data-reactid afik.

@@ -52,7 +52,7 @@ describe('ReactDOMComponent', function() {
var stubStyle = container.firstChild.style;

// set initial style
var setup = {display: 'block', left: '1', top: 2, fontFamily: 'Arial'};
var setup = {display: 'block', left: '1px', top: 2, fontFamily: 'Arial'};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this about? Behavior change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Styles may be unitless if they are integers, but strings must specify units if required (unitless strings may fire a warning). This looks fine to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jimfb recently merged a PR that adds a warning for this case but it only checked the create case, not the update case which we use for createElement. I fixed that so the test for the warning passes with useCreateElement and fixed this test to not warn.

@sophiebits
Copy link
Collaborator Author

@jimfb I'm operating under the assumption that browser extensions will insert extra nodes into our markup and we need to skip over them.

@sebmarkbage
Copy link
Collaborator

Make sure you do your react-native homework

@syranide
Copy link
Contributor

My hope is that we can kill data-reactid entirely especially when using server-side rendering. Rehydrating a markup tree does not benefit from having data-reactid afik.

@jimfb FYI, we can already kill data-reactid for server-rendered markup, it's rather trivial as long as you're ok with doing a fast pass over all the nodes once when starting the client.

@facebook-github-bot
Copy link

@spicyj updated the pull request.

@sophiebits
Copy link
Collaborator Author

We can do that trivially here too by changing precacheChildNodes to not look at IDs and just match up the nodes – but I'm worried about finding extra nodes.

@syranide
Copy link
Contributor

@spicyj We can always mark the nodes with just data-react, but leave out the ID. That should give you most of the benefits, while also tolerating foreign nodes. Personally I think we should just be super strict and simply not allow unexpected nodes, they are crashes waiting to happen.

@sophiebits
Copy link
Collaborator Author

That's true, but I don't think including a numeric ID (data-reactid="17") is much overhead over that.

@jimfb
Copy link
Contributor

jimfb commented Oct 16, 2015

Personally I think we should just be super strict and simply not allow unexpected nodes, they are crashes waiting to happen.

I tend to agree - if we find an unexpected node while restoring a SSR DOM tree, we can just kill it with fire. We don't even need a flag on the nodes to do that.

@syranide
Copy link
Contributor

That's true, but I don't think including a numeric ID (data-reactid="17") is much overhead over that.

@spicyj data-react compresses trivially to virtually nothing, but yeah the overhead of the ID should be minimal too. Both are miles better than the current reactids anyway.

@sophiebits sophiebits force-pushed the cotree branch 3 times, most recently from de4a18f to 7ace2b9 Compare October 18, 2015 01:31
@facebook-github-bot
Copy link

@spicyj updated the pull request.

@sophiebits
Copy link
Collaborator Author

@jimfb @sebmarkbage The last two commits (edit: more by the time you read this, start with the one called "Events:") add events support so we don't rely on hierarchical IDs for events either. (Sorry for piling it all into the same PR – but github sucks at stacking PRs and I didn't want to merge this to master quite yet.) Either of you want to take a look? I'm a little nervous but all the FB test flows worked the first time with this applied.

@facebook-github-bot
Copy link

@spicyj updated the pull request.

@sophiebits
Copy link
Collaborator Author

Did a quick benchmark of rendering a PE-like hierarchy with Electron (Chrome) with a cold and warm JIT using my perf-counters as the timestamp.

With a warm JIT (rendering the hierarchy 100 times before measuring anything):

This PR is 3.3% faster than master.
Switching to numerical IDs (will be a separate PR, not quite done yet) brings us to 7.9% faster than master.
Numerical IDs with createElement instead of HTML generation is on par (also 7.9%).
Numerical IDs with createElement and not setting data-reactid at all (since we don't need it) gives another 1.4% boost for a total of 9.1%.

With a cold JIT, createElement looks a little bit slower (but this PR helps more compared to master!).

Our deltas of
[3.3%, 7.9%, 7.9%, 9.1%] with a warm JIT are
[4.9%, 9.6%, 8.3%, 8.9%] with a cold JIT.

Not sure if there are things we can do to improve the speed of createElement.

@syranide
Copy link
Contributor

@spicyj Just for context, I assume those are React rendering times + innerHTML but do not include browser rendering/painting? PS. Also that's awesome :)

@sophiebits
Copy link
Collaborator Author

var a = now();
React.render(React.createElement(Benchmark), container);
container.offsetHeight;
var b = now();

There's probably some painting involved. I intentionally forced layout: I wanted to make sure all the markup was parsed to make sure the HTML mode wasn't doing stuff lazily. Any ideas on how to keep that guarantee while avoiding painting (and layout)?

@syranide
Copy link
Contributor

@spicyj I'd assume that would just force layouting, realistically rendering/painting should only happen after JS execution ends. I'm pretty sure innerHTML is not deferred as you can measure differences in time by the number of tags/attributes/styles/etc present. It probably doesn't really make sense to defer it in practice either I would think.

@sophiebits
Copy link
Collaborator Author

You're probably right. I tried it again without the forced layout and it's about 24ms (on master: 210ms vs 186ms) faster on all five numbers, so the percentages are proportionally the same but go up a little because of a smaller base. Cold JIT, just React.render (no layout): [5.5%, 10.8%, 9.2%, 10.0%].

@sophiebits
Copy link
Collaborator Author

Numerical IDs are in #5205. Also, devtools don't work with this because they currently rely on ReactMount.getNode.

Initial render can still be a markup string.
New module! With this, we have a new module that uses the component tree to store DOM nodes. Most of the logic in this file relates to markup adoption -- if we were to drop that (along with server rendering) this file could just be a `return inst._nativeNode;`.

This works with useCreateElement only because we need to reference each node as it's created.

Events is now the only thing using ReactMount.getNode -- I'll introduce pointers back from the DOM nodes (and a `ReactDOMComponentTree.getInstanceFromNode`) and make that work.
Next step: take advantage of having the native instances in EventPropagators instead of converting right back to IDs.
This removes SimpleEventPlugin's dependency on ReactMount.getID.
@facebook-github-bot
Copy link

@spicyj updated the pull request.

@sophiebits
Copy link
Collaborator Author

Merged in #5205.

@sophiebits sophiebits closed this Nov 4, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants