facebook/react-devtools

App crashes upon the automatic attempt to inspect hooks on a Fiber with tag of type ClassComponent

shirakaba opened this issue · 3 comments

Background

I'm building a custom React renderer for NativeScript, React NativeScript. It's based on react-reconciler.

I've begun integrating devtools, using injectIntoDevTools, in my top-level ReactNativeScript.render() method.

I believe that my integration, at least, is correct, as it's consistent with various other custom renderers.

I'm using:

  • react 16.8.5
  • react-reconciler 0.20.2
  • react-devtools 3.6.1

Issue

Symptoms

The devtools work to some extent (as my screenshot shows):

image

... however, my app crashes just a few milliseconds after this moment, in which I've just highlighted <RNTesterApp> in the hierarchy. You can see that the dev tools display: "Hooks: Loading..." just before the app crashes.

My app reports the following error:

Error: Unknown Fiber. Needs to be a function component to inspect hooks.

... and stack trace:

***** Fatal JavaScript exception - application has been terminated. *****
Native stack trace:
1   0x1073b2c4f NativeScript::reportFatalErrorBeforeShutdown(JSC::ExecState*, JSC::Exception*, bool)
2   0x1073e82c0 NativeScript::FFICallback<NativeScript::ObjCMethodCallback>::ffiClosureCallback(ffi_cif*, void*, void**, void*)
3   0x107d0bd06 ffi_closure_unix64_inner
4   0x107d0c72a ffi_closure_unix64
5   0x10ab38ccf _dispatch_call_block_and_release
6   0x10ab39d02 _dispatch_client_callout
7   0x10ab45a50 _dispatch_main_queue_callback_4CF
8   0x10a55a8a9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
9   0x10a554f56 __CFRunLoopRun
10  0x10a554302 CFRunLoopRunSpecific
11  0x1116ab2fe GSEventRunModal
12  0x10ba39ba2 UIApplicationMain
13  0x107d0c56d ffi_call_unix64
14  0x120b38250
JavaScript stack trace:
1   inspectHooksOfFiber@file:///app/vendor.js:20284:89
2   inspectHooksTree@file:///app/vendor.js:27252:111
3   @file:///app/vendor.js:27218:35
4   emit@file:///app/vendor.js:24580:25
5   forEach@[native code]
6   @file:///app/vendor.js:19200:29
7   forEach@[native code]
8   _handleMessage@file:///app/vendor.js:19191:31
9   _handleMessage@[native code]
10  @file:///app/vendor.js:27991:11
11  forEach@[native code]
12  handleMessage@file:///app/vendor.js:27989:29
13  _notifyBrowser@file:///app/vendor.js:16818:36
14  webSocketDidReceiveMessage@file:///app/vendor.js:16686:29
15  UIApplicationMain@[native code]
16  _start@file:///app/vendor.js:34930:26
17  run@file:///app/vendor.js:34962:11
18  startWithView@file:///app/bundle.js:1601:22
19  @file:///app/bundle.js:10337:76
20  ./app.ts@file:///app/bundle.js:10364:34
21  __webpack_require__@file:///app/runtime.js:751:34
22  checkDeferredModules@file:///app/runtime.js:44:42
23  webpackJsonpCallback@file:///app/runtime.js:31:39
24  anonymous@file:///app/bund<…>

Cause of error

I've tracked down the error message to this line in backend/ReactDebugHooks.js:

if (
  fiber.tag !== FunctionComponent &&
  fiber.tag !== SimpleMemoComponent &&
  fiber.tag !== ForwardRef
) {
  throw new Error(
    'Unknown Fiber. Needs to be a function component to inspect hooks.',
  );
}

I found that the condition for throwing the error was satisfied because the fiber had tag 1 (which, as I'm using React 16.8.5, corresponds to ReactTypeOfWork.ClassComponent). The fiber is indeed <RNTesterApp>, as it shares the same props.

So it seems that the app is throwing an error because the dev tools have attempted to inspect hooks on a class component, and that's not apparently a supported action for the devtools backend.

Odd aspects

  • I'd expect at most a no-op rather than an app crash.

  • The class component in question doesn't actually have any hooks to inspect.

  • The devtools have successfully drilled down into the class components <_Frame> and <_Page> already, so I can't see why <RNTesterApp> would pose a problem where these two didn't.

  • inspectHooksOfFiber() is only called upon trying to inspect <RNTesterApp>; it's not called when inspecting any of the parent components that I opened on the way. I do wonder what the difference is.

Replication of error

I'm happy to provide more information. As this is a custom renderer, the best I can provide for a Minimum Working Example is the RNTesterApp in the sample folder in my repo on the devtools2 branch. The code can be run with:

git clone https://github.com/shirakaba/react-nativescript.git
cd react-nativescript
git checkout devtools2

# Yes, there's a sub-directory with the same name as the project.
cd react-nativescript
npm install
cd ../sample
npm install

cd app
git clone https://github.com/shirakaba/react-nativescript-compat-react-native.git
mv react-nativescript-compat-react-native RNTester
cd ..

## The `tns` CLI is provided by installing NativeScript globally.
## Full walkthrough here: https://docs.nativescript.org/start/quick-setup
# npm install -g nativescript
tns plugin add nativescript-websockets
tns debug ios --bundle --syncAllFiles --emulator

# Now, in a separate terminal:
react-devtools

I'm happy to provide any more details needed! Really hoping to get to the bottom of this.

i am running into the same issue in working with the react-blessed custom renderer. from my debugging, i'm finding that the value of FunctionComponent at

if (
fiber.tag !== FunctionComponent &&
fiber.tag !== SimpleMemoComponent &&
fiber.tag !== ForwardRef
) {
throw new Error(
is 0, which is different from the value of FunctionComponent at
var containsHooks =
(tag === FunctionComponent ||
tag === SimpleMemoComponent ||
tag === ForwardRef) &&
!!fiber.memoizedState;
, where it is 1.

This causes containsHooks to be true for a class component when it should not be.

ReactDebugHooks has a module-level constant, whereas attachRendererFiber obtains this constant from evaluating the version of the renderer.

at least, these two should be consistent.

however, it doesn't seem like the renderer version should be the pivot since a custom renderer will have an unrelated version number. it seems like the version of the reconciler is what's desired, but the conditions are sensitive to the react version.

the official react renderers all get their version from https://github.com/facebook/react/blob/master/packages/shared/ReactVersion.js#L11

for me, having my devtoolsConfig pretend to be the version of react from which react-reconciler came (16.8.6 for me) got past this issue and devtools loaded successfully.

That’s a very good idea! I’ll apply your versioning trick next time I return my attention to this.

These dev tools are being replaced with a new version soon, so I’ve been told to wait until that comes out and re-assess the situation (as this repo won’t be receiving much attention). If necessary, I can re-file the issue if they do migrate to a new repo.

React DevTools has been rewritten and recently launched a new version 4 UI. The source code for this rewrite was done in a separate repository and now lives in the main React repo (github.com/facebook/react).

Because version 4 was a total rewrite, and all issues in this repository are related to the old version 3 of the extension, I am closing all issues in this repository. If you can still reproduce this issue, or believe this feature request is still relevant, please open a new issue in the React repo: https://github.com/facebook/react/issues/new?labels=Component:%20Developer%20Tools