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

Optimizing Compiler: Tagging ReactElements #3227

Open
sebmarkbage opened this Issue Feb 22, 2015 · 6 comments

Comments

Projects
None yet
4 participants
@sebmarkbage
Member

sebmarkbage commented Feb 22, 2015

We can make more optimized reconciliation by tagging ReactElements with the "hidden class" of their props.

For example, this is guaranteed to always have three props: className, width, children.

<div className="foo" style={{ width: w, height: 100 }}>{c}</div>

If we could tag every element with these properties with a unique ID:

{ __t: 7, type: 'div', props: { className: 'foo', style: { width: w, height: 5 }, children: c } }

Then we could use the hidden class to generate an optimized diffing algorithm for these instead of iterating over the properties. Presumably, we would only need to do this for type: <string> since we only diff native components.

Bonus points if we can determine which properties are constant. Perhaps using a property descriptor object:

// Constant properties are annotated as 1, other properties are excluded and inferred by props.
var t = { className: 1, style: { height: 1 } };
{ __t: t, type: 'div', props: { className: 'foo', style: { width: w, height: 5 }, children: c } }

We would use a heuristic inside React to determine when to create an optimized differ. For example, after 10+ updates to the same component. Just like a JIT would do.

if (oldElement.__t === newElement.__t) {
  numberOfUpdates++;
} else {
  numberOfUpdates = 0;
}

if (numberOfUpdates === 10) {
  optimizedDiffer = generateOptimizedDiffer(newElement);
  optimizedDiffer(oldElement, newElement);
} else if (numberOfUpdates > 10) {
  optimizedDiffer(oldElement, newElement);
} else {
  manualDiffing(oldElement, newElement);
}
@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Feb 23, 2015

Contributor

If we could tag every element with these properties with a unique ID:

This seems problematic with watcher builds and generally keeping bundle hashes static for production. Or is this intended to be a production build post-process of some sort? But even then there are problems when separate parts of apps are built/maintained individually.

Then we could use the hidden class to generate an optimized diffing algorithm for these instead of iterating over the properties. Presumably, we would only need to do this for type: since we only diff native components.

This would be very useful for non-DOM elements too I would think (but it would have to be opt-in).

Bonus points if we can determine which properties are constant. Perhaps using a property descriptor object:

It seems to me that you would also want __ts to reference a shared unique ID which would identify the unique set of actual props to check or you'll end up with a potential of O(2^n) differs rather than O(n), n being unique sets of prop names to check.

We would use a heuristic inside React to determine when to create an optimized differ. For example, after 10+ updates to the same component. Just like a JIT would do.

Shouldn't this be globally per __t rather than per instance?

Contributor

syranide commented Feb 23, 2015

If we could tag every element with these properties with a unique ID:

This seems problematic with watcher builds and generally keeping bundle hashes static for production. Or is this intended to be a production build post-process of some sort? But even then there are problems when separate parts of apps are built/maintained individually.

Then we could use the hidden class to generate an optimized diffing algorithm for these instead of iterating over the properties. Presumably, we would only need to do this for type: since we only diff native components.

This would be very useful for non-DOM elements too I would think (but it would have to be opt-in).

Bonus points if we can determine which properties are constant. Perhaps using a property descriptor object:

It seems to me that you would also want __ts to reference a shared unique ID which would identify the unique set of actual props to check or you'll end up with a potential of O(2^n) differs rather than O(n), n being unique sets of prop names to check.

We would use a heuristic inside React to determine when to create an optimized differ. For example, after 10+ updates to the same component. Just like a JIT would do.

Shouldn't this be globally per __t rather than per instance?

@sophiebits

This comment has been minimized.

Show comment
Hide comment
@sophiebits
Member

sophiebits commented Aug 28, 2015

cc @jimfb

@trueadm

This comment has been minimized.

Show comment
Hide comment
@trueadm

trueadm Jan 22, 2016

Contributor

@sebmarkbage why not instead use the hidden class object to reference props that are dynamic rather than static ones?

var t = { children: 1, style: { width: 1 } };
{ __t: t, type: 'div', props: { className: 'foo', style: { width: w, height: 5 }, children: c } }

Then you'd easily be able to loop through only these props upon doing a patch of the ReactElement.

You could go one step further (would affect backwards compatibility) and make the original ReactElement only return dynamic props plus the hidden class reference. The hidden class would reference all the static props.

Furthermore, you can work out what is static at compile time, depending on how much computation time you want to spend in the compilation stage, for example the following would return as static:

'someString'
1 + 1
foo + 'bar' (given foo is a const)

There are plenty more too, but those are the easy ones to find in the AST.

Contributor

trueadm commented Jan 22, 2016

@sebmarkbage why not instead use the hidden class object to reference props that are dynamic rather than static ones?

var t = { children: 1, style: { width: 1 } };
{ __t: t, type: 'div', props: { className: 'foo', style: { width: w, height: 5 }, children: c } }

Then you'd easily be able to loop through only these props upon doing a patch of the ReactElement.

You could go one step further (would affect backwards compatibility) and make the original ReactElement only return dynamic props plus the hidden class reference. The hidden class would reference all the static props.

Furthermore, you can work out what is static at compile time, depending on how much computation time you want to spend in the compilation stage, for example the following would return as static:

'someString'
1 + 1
foo + 'bar' (given foo is a const)

There are plenty more too, but those are the easy ones to find in the AST.

@sophiebits

This comment has been minimized.

Show comment
Hide comment
@sophiebits

sophiebits Jan 22, 2016

Member

@trueadm I think @sebmarkbage mentioned that originally with "Bonus points if we can determine which properties are constant.".

Member

sophiebits commented Jan 22, 2016

@trueadm I think @sebmarkbage mentioned that originally with "Bonus points if we can determine which properties are constant.".

@trueadm

This comment has been minimized.

Show comment
Hide comment
@trueadm

trueadm Jan 22, 2016

Contributor

@spicyj wasn't that referring to the hidden class only referencing props that were constant/static? Sorry, maybe I misunderstood what point of mine you were referencing.

Contributor

trueadm commented Jan 22, 2016

@spicyj wasn't that referring to the hidden class only referencing props that were constant/static? Sorry, maybe I misunderstood what point of mine you were referencing.

@sophiebits

This comment has been minimized.

Show comment
Hide comment
@sophiebits

sophiebits Jan 22, 2016

Member

Oh, I totally misread. You're right, my apologies.

Member

sophiebits commented Jan 22, 2016

Oh, I totally misread. You're right, my apologies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment