[Fiber] Support React DevTools#8818
Conversation
| typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && | ||
| __REACT_DEVTOOLS_GLOBAL_HOOK__.supportsFiber | ||
| ) { | ||
| __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ |
There was a problem hiding this comment.
Looks like I'll have to move this back to the renderer.
It will need access to the component tree for "jump to component based on DOM node" feature.
|
|
||
| src/renderers/shared/hooks/__tests__/ReactComponentTreeHook-test.js | ||
| * can be retrieved by ID | ||
| * works |
There was a problem hiding this comment.
Why is this now failing? It seems like you didn't do anything to disable existing behavior.
There was a problem hiding this comment.
Test is now erroring with TypeError: Set is not a constructor whereas before it was just failing with an expectDev
There was a problem hiding this comment.
I use Set in DEV mode earlier so the test that specifically removed global.Set now fails. We use Set in other places anyway so I think this doesn’t really matter. We should fix those together (or depend on a polyfill).
| var warning = require('warning'); | ||
|
|
||
| var devCommitListeners = []; | ||
| var devMountedRoots = new Set(); |
There was a problem hiding this comment.
This is unfortunate because it removes a neat GC clean up opportunity (although many subscriptions will hold onto the tree regardless). At least if a whole subtree can be GC:ed, right now we get the clean up automatically (if someone forgets unmountComponentAtNode).
The __REACT_DEVTOOLS_GLOBAL_HOOK__ is always available regardless if the tool is available or not. That gives us an opportunity to react to its existence. We know that we'll never need to drop any roots because we can just pass them along to the __REACT_DEVTOOLS_GLOBAL_HOOK__ and it can decide whether we need to keep them around for when the devtools starts.
There was a problem hiding this comment.
The REACT_DEVTOOLS_GLOBAL_HOOK is always available regardless if the tool is available or not.
Oh, that's neat! I didn't realize.
| var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber'); | ||
| var warning = require('warning'); | ||
|
|
||
| var devCommitListeners = []; |
There was a problem hiding this comment.
In practice, since this only attaches to __REACT_DEVTOOLS_GLOBAL_HOOK__ there will only ever need to be one listener, which is the __REACT_DEVTOOLS_GLOBAL_HOOK__ itself. It might be simpler just do track it as const listener = __REACT_DEVTOOLS_GLOBAL_HOOK__ and call on it.
That way this source can be smaller for when we put it in prod.
2d2fab5 to
ca38d3f
Compare
|
Ready for another review. |
| // This way older DevTools can ignore newer Fiber versions. | ||
| version: 1, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
I tend to prefer putting exports outside the conditional at the top level so that we can easily convert this to ES modules. Also guarantees that all names are always exported and constant bindings.
| __REACT_DEVTOOLS_GLOBAL_HOOK__.onCommitFiberRoot(rendererID, root); | ||
| } catch (err) { | ||
| // Catch all errors because it is unsafe to throw in the commit phase. | ||
| warning(false, 'React DevTools encountered an error: %s', err); |
There was a problem hiding this comment.
Aren't all warnings supposed to be __DEV__ only so we can strip the warning module and its entire system in PROD? This is available in PROD.
There was a problem hiding this comment.
My apologies. I was unaware of this.
I think there are a couple of other places where we have warning outside of __DEV__ (eg ReactDOMFiberSelect). Should we clean these up?
There was a problem hiding this comment.
I think the module itself is disabled in PROD and we should probably ensure that it gets fully stripped with a transform like we do for invariant messages.
It was just unclear what was intended here.
There was a problem hiding this comment.
I enabled it in prod per Ben's feedback.
There was a problem hiding this comment.
Oh, I didn't realize we are putting all warnings behind DEV though. Somehow I though Babel transform is doing it. I'll add a condition. (I think it's fine to crash DevTools silently when using DevTools in prod)
I can also move the try-catch to the DevTools.
| ) { | ||
| exports.injectInternals = function(internals : Object) { | ||
| warning(rendererID == null, 'Cannot inject into DevTools twice.'); | ||
| rendererID = __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ |
There was a problem hiding this comment.
I wonder if we should capture __REACT_DEVTOOLS_GLOBAL_HOOK__.inject et all in constant closure variables to make it type safe and easily inlinable by the VM. Currently, it's not since both the global and the object itself could've been mutated.
| ...internals, | ||
| // Increment when internals become incompatible with DevTools. | ||
| // This way older DevTools can ignore newer Fiber versions. | ||
| version: 1, |
There was a problem hiding this comment.
I'm not sure it makes sense to put the version here. 1) It adds runtime Object.assign cost and more code. Probably resulting in an unoptimized object atm. 2) The version indicates what API is exposed, but that API is defined by the DOM renderer here. If you bump this you have to make sure that all consumers and downstream renderers bump theirs too. So isn't the current version more associated with the DOM renderer rather than this shared helper?
I'm also skeptical about version numbers being super useful here anyway. Often we'll want to add features to the devtools that work with minor already released versions without the ability to bump this so we would and those features might not work with the whole range of the version.
So I think we'll probably just end up with feature detection anyway.
There was a problem hiding this comment.
If you bump this you have to make sure that all consumers and downstream renderers bump theirs too.
Why? The way I imagined, every time we make an incompatible change, we can fork the code in DevTools (and eventually start removing old forks). The only difference is this is now explicit. The downstream renderers would update it at the same time they update their reconciler version (which is really what version field specifies).
There was a problem hiding this comment.
My main intention is for old DevTools versions to not crash when they attach to new Fiber versions that, for example, have different children data structure. In the past, data structures barely changed, but I'm not sure this will be true for Fiber (e.g. hybernating trees). So my intention is that Fiber can say "hey, I'm the new incompatible version" and then old DevTools just bail rather than crash.
We can't feature test that because we can't modify old versions of DevTools once they're out. But maybe it doesn't matter because they auto update?
There was a problem hiding this comment.
Another example of incompatibility is changing types of work enum. If old tools can't just bail they might display everything incorrectly and it wouldn't be obvious they need to be updated.
There was a problem hiding this comment.
In your work enum case, it's similarly not obvious that you need to bump the version number when you make this change. Effectively, we'd just have to bump the version number all the time, meaning we also have to make devtools opt in all the time for every change even though most of them keeps working just fine.
And if we add features to devtools after the fact, we'd end up with things relying on internals anyway.
There was a problem hiding this comment.
it's similarly not obvious that you need to bump the version number when you make this change
I'll catch you in code review! 😄
But yea, I see your point.
This is fine because we don't use it in Stack anyway (at least not that part), and we also rely on Set/Map in other parts of code. The newly failing test is the one saying "it works without Map/Set".
This lets us ignore pre-Fiber DevTools instead of crashing them by injecting a different API.
39749c9 to
5c2456e
Compare
2e84c1a to
12f5736
Compare
|
I'll do RN and Art as a followup. I think this is ready to get in. |
TODO:
alternateto avoid diffing Support Fiber react-devtools#475 (comment)This adds initial support for React DevTools for Fiber.
The corresponding React DevTools PR is facebook/react-devtools#475.
Historical notes
I initially tried to implement the diffing on React side (#8807), but after discussion with the team, decided against it to satisfy these constraints:
If the architecture seems fine we should be able to merge this PR, and keep iterating on facebook/react-devtools#475 separately. The idea is that: