mobx: DisplayName not being forwarded when wrapped in

When I receive a printed call stack from React, the components wrapped in observer show up as observerComponent

    at observerComponent (http://localhost:3000/static/js/bundle.js:119423:69)
    at div
    at observerComponent (http://localhost:3000/static/js/bundle.js:119423:69)
    at PrimaryWindow (http://localhost:3000/static/js/bundle.js:4346:5)

and in the callstack in the debugger shows up as anonymous

<anonymous> (/Users/johntwigg/development/crypto/loopNFT/ui/src/components/Cinema/CinemaMain.tsx:35)
<anonymous> (/Users/johntwigg/development/crypto/loopNFT/ui/node_modules/mobx-react-lite/src/observer.ts:104)
<anonymous> (/Users/johntwigg/development/crypto/loopNFT/ui/node_modules/mobx-react-lite/src/useObserver.ts:115)

Intended outcome: Ideally, the underlying React Name would be maintained. CinemaMain in this case.

Actual outcome: anonymous and observerComponent

Looking at issue #3422 doesn’t seem to resolve or change the behavior for me. For example

const FocusLane : React.FC = observer(() => {...})
FocusLane.displayName = "FocusLane"

has no impact.

Any ideas? I’m open to workarounds.

Versions

➜  ui git:(mobx) ✗ npm list --depth 2 | grep mobx
├─┬ mobx-react-lite@3.4.0
│ ├── mobx@6.6.1

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 17 (3 by maintainers)

Most upvoted comments

I just spent a good couple of hours debugging this problem, and here’s what I’ve found.

The biggest issue is that React doesn’t respect the displayName property any more, which appears to be an intentional change as part of what they call Native Component Stacks, in which they use an actual stack trace to generate their stacks. IMHO, this is a mistake as it makes debugging with higher order components like observer very difficult. I have weighed in on the relevant issue here: https://github.com/facebook/react/issues/22315 (don’t let the name fool you, it happens in every browser).

To work around this, as suggested above, you can instead redefine name instead of displayName. Note this must be done via Object.defineProperty and not a vanilla assignment. Unfortunately, this doesn’t work in Firefox, because Firefox stacks ignore name, and that’s where React is getting its info from. Still, as I will grudgingly admit to Chrome’s overwhelming popularity, that might be a workaround for now.

On top of all this, if you’re using the “set the display name after defining the component” strategy (option 2 on what I consider the definite source on this, https://github.com/mobxjs/mobx/issues/141#issuecomment-470883978), this still doesn’t work. The reason for this is that you’re changing the displayName (or if working around, the name) of the memo which wraps the actual component, and React, through some black magic, unwraps the actual component when computing the stack trace, so the name is still missing.

To work around this, I had to author the following dubious code:

export function addDisplayName(component, displayName) {
    if (component.$$typeof.toString() === "Symbol(react.memo)") {
        // Memo objects have a `type` underlying property which is the thing they're wrapping
        component.type.displayName = displayName        
    }

    component.displayName = displayName
}

Would it be possible to define displayName on the memo as a bidirectional computed property (set and get) which would just proxy the underlying wrappedComponent’s displayName? I haven’t investigate this yet but maybe it would be possible.