-
Notifications
You must be signed in to change notification settings - Fork 164
Added new ReactARTFiber target based on ReactFiberReconciler #105
Conversation
sebmarkbage
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't really land this as is to replace ReactART. Maybe we should land something as ReactARTFiber first so that we can test it out first?
| var MAX_VEL = 11; | ||
| var CLICK_ACCEL = 3; | ||
| var BASE_VEL = 0.15; | ||
| const React = require('react'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we split this file out into a separate PR? It's unclear if you needed to change something here to support the new renderer or if it is just a style change. Regardless it's unrelated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. This file was causing some console warnings before:
Warning: bind(): React component methods may only be bound to the component instance. See VectorWidget
No changes were required though. I just fixed the warnings and added a little better react-art component coverage. I can do both via a separate PR.
src/ReactART.js
Outdated
|
|
||
| this._mountNode = ARTRenderer.mountContainer( | ||
| this.props.children, | ||
| container, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This no longer passes context along. I think we'll need to provide the third argument here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be a stupid question...but...why do ReactART components need context? The set of components being managed by the ARTRenderer only includes the ones explicitly defined in this module, none of which use context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it can pass through from components above. That's how we use it at FB.
class Foo {
getChildContext() {
return { foo: 123 };
}
render() {
return <div><Surface><Group><Bar /></Group></Surface></div>;
}
}
class Bar {
render() {
return <Group><Path /><Text>{this.context.foo}</Text></Group>;
}
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. That makes sense. Thanks!
src/ReactART.js
Outdated
| props.path | ||
| ); | ||
| this._oldString = newString; | ||
| ARTRenderer.updateContainer(this.props.children, this._mountNode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also needs to pass the third argument.
src/ReactART.js
Outdated
| return instance; | ||
| }, | ||
|
|
||
| createTextInstance(text, internalInstanceHandle) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that Fiber will turn any array of strings into createTextInstance calls. So I think that something like <Text>{"foo"}{"bar"}</Text> will break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. You are correct.
I'm unsure of how to best handle this since the text param is just a string with no associated props (eg font, color). At a glance, I think this information is accessible via internalInstanceHandle.return.pendingProps but I'm not sure if that would be acceptable or hacky, eg:
createTextInstance(text, internalInstanceHandle) {
const props = Object.assign(
{},
internalInstanceHandle.return.pendingProps,
{ text }
);
return new Text(props);
},There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, one option could be to make Text a composite that flattens the children and renders the lower-level host text component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seb probably has other ideas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think I'll go with something like that after trying this as a first pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I may punt on supporting custom components that return text for this initial iteration. We can always add support for that in a follow-up.
The current API makes it difficult to associate a text string with its parent instance. I initially tried:
createTextInstancereturnstext(string).appendInitialChildhandles special case wherechildis a string by appending to an array stored on the ART textinstance(for lack of a better place).- When applying props for text, use the aforementioned array whenever
props.childrenis not a string.
Unfortunately commitTextUpdate only gets the updated text string and has no reference to the ART instance, so no way to update the text-array.
Also, createTextInstance is called before the ART instance is created and so the only thing I have access to at that point is the internalInstanceHandle which I shouldn't be crawling in the renderer. This also complicates the initial display because- without using internalInstanceHandle- there's no way to gather up text-children and pass them along to the eventual ART text instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You would need to have a place holder object for text instances with a parent pointer and store an expando with the list of text instances on the Text ART instance. So that when commitTextUpdate happens, you can do textInstance.text = newText; textInstance.parent.draw(textInstance.parent._textInstances.map(ti => ti.text).join(''))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we need an alternative text API that makes it easier for certain renderers. I'm not sure if it can be much more efficient than this though since these can be nested deeply. So maybe a helper lib on top of host config would be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that as well, but it didn't work for reasons I tried to explain above. (Maybe one of us is misunderstanding each other?) If it would be easier to chat on Hangouts I could. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You would need to have a place holder object for text instances with a parent pointer
The problem is that when the text is first created (createTextInstance) I only have access to the text string itself and the internalInstanceHandle. I could crawl the return pointers to find the parent text node- but that's probably not a good idea to do in the renderer.
Also, the ART Text instance doesn't exist yet at this time (eg createInstance hasn't been called) so I would have to use the internal handle for temporary storage and then bundle along the text strings to the eventual ART instance when createInstance is called.
Overall it felt dirty/hacky.
src/ReactART.js
Outdated
| /** COMPONENTS */ | ||
|
|
||
| const ContainerMixin = assign({}, ReactMultiChild, { | ||
| class Node { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have a wrapper class around every instance. However, they don't seem to have much interesting state associated with them so we're allocating them only to store a pointer to the ART instance.
Could you just use the ART instance as the instance and rely on pattern matching to pick the logic branch instead of OO methods?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally. I've removed the wrapper objects. The metadata I was storing on the wrapper instance (eg current Text string, previous path delta, etc) is now just being stored on the ART-created instance. Kind of hacky but it avoids the wrapper object.
src/ReactART.js
Outdated
| injectAfter(this.node, afterNode, childNode); | ||
| }, | ||
| this._listeners = {}; | ||
| this._subscriptions = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to store subscriptions because we can just rely on garbage collection to do the clean up for us. In fact, _removeEventListeners isn't even called.
I suppose the current ART API requires us to store the subscriptions to unsubscribe when you swap them out during updates though. Maybe we can just store them as an expando property on the ART instance as a hack?
In the normal case we don't actually use many of these listeners anyway. We attach top level listeners at the root and dispatch it ourselves.
src/ReactART.js
Outdated
| instance = new Shape(props); | ||
| break; | ||
| case TYPES.TEXT: | ||
| instance = new Text(props); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does Text handle props.children as well? since <Text>Hello</Text> will not create a text instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Text handles single-children (eg <Text>Hello</Text>) and arrays of children (eg <Text>Hello, {name}</Text>).
Sure! I added a new |
| const Transform = require('art/core/transform'); | ||
| const invariant = require('fbjs/lib/invariant'); | ||
| const React = require('react'); | ||
| const ReactFiberReconciler = require('react-dom/lib/ReactFiberReconciler'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious, are you testing this against React@master?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Pulled as recently as an hour or two ago. Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was curious about your setup for running against master. I was thinking of starting a fiber implementation for https://github.com/iamdustan/react-hardware.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh! Gotcha. 😄
I tried using npm link at first but ran into problems with Babel trying to reprocess the dist for 'react' and 'react-dom'. Seems like the symlink created by npm link caused the exclude for 'node_modules' not to work for those packages. So I gave up on that after a while.
What I ended up doing was a bit of a hack- but it worked, so...
I checked out React master and copied the built versions of 'react' and 'react-dom' into the node_modules folder of 'react-art'. So basically...
cd path/to/react
cp -r ./build/packages/react path/to/your/project/node_modules/
cp -r ./build/packages/react-dom path/to/your/project/node_modules/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! 😄
I wonder if http://react.zpao.com/builds/master/latest/ is still kept up to date for a daily sync, too...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That looks like UMD builds, which won't provide a way to load the new fiber reconciler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the react-dom.tgz files are the packages for npm assets...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
|
Edit: Unfortunately this failed due to the error: |
|
String refs should work; grep for coerceRef. |
|
This change set was approved in Phabricator. I'm going to merge here to keep Github in sync. |
Also updated existing unit tests to run against both legacy and fiber ReactART builds.
The new target also fixes a bug with event handlers that exists in the legacy target. I believe the existing react-art renderer intended to use
handleEventas the event subscriber but was actually using theprops.listener. This mostly worked, but failed in the case where a specific event handler (egonClick) changed.While testing, I noticed that the
ClippingRectangletarget doesn't work quite right for SVG mode. This is not a regression. It's something that needs to be fixed in the underlying Art library (if we care).