-
Notifications
You must be signed in to change notification settings - Fork 47.2k
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
Bug: Native Component Stacks don't respect function "displayName" in Firefox #22315
Comments
Just to be clear, ignoring the
I appreciate the repro! Would it be possible to get one that doesn't include any external packages (like react-easy-state)? (It's always nice for the repro to be as small and self-contained as possible.) Digging into the third party library you mentioned, I don't think the problem is actually "inheritance" at all but rather the way the component is being rendered in the HOC: // create a memoized reactive wrapper of the original component (render)
// at the very first run of the component function
const render = useMemo(
() =>
observe(Comp, {
scheduler: () => setState({}),
lazy: true,
}),
// Adding the original Comp here is necessary to make React Hot Reload work
// it does not affect behavior otherwise
[Comp],
); For example, this simplified repro shows the behavior you describe: But this one does not: That's because the latter approach is not actually rendering |
Thank you for the fast response!
I think you're spot on that this is about render prop vs React element. Sorry, I confused the terminology. So it looks like moving away from render props could be a workaround for now. I'm not sure if this is actually possible for react-easy-state though, but I can try. One unfortunate aspect about this issue is that the docs still imply that |
Can you share a reduced repro that shows this behavior specifically? Looking at the code, it seems like the react/packages/shared/ReactComponentStackFrame.js Lines 260 to 262 in 50263d3
|
Here you go: https://codesandbox.io/s/naughty-austin-jk28f?file=/src/App.tsx It's basically the renderprop example you linked above, but instead of react-error-boundary I'm using a custom ErrorBoundary component (based on example code from somewhere in the docs). We still have the two components |
Ah, sorry. To be clear that is the expected behavior for the |
I think there is a miscommunication here. <ErrorBoundary>
<WrappedBadGuy />
</ErrorBoundary> What's being passed as a render prop is the inner function function view(Comp: any) {
let ReactiveComp = (props: any) => {
return Comp(props);
};
return ReactiveComp;
}
const WrappedBadGuy = view(function _WrappedBadGuy() {
throw Error("sorry");
});
(WrappedBadGuy as any).displayName = "WrappedBadGuyDisplayName"; |
Ah, you are correct. I missed this line: (WrappedBadGuy as any).displayName = "WrappedBadGuyDisplayName"; I don't think I see how the Seems like what you're really reporting is that a component like this: function BadGuy() {
throw Error("sorry");
}
BadGuy.displayName = 'BadGuyDisplayName'; Will show up as In other words, it will show this:
Here is a reduced repro: |
Somehow it never occurred to me to test this behavior without some kind of HOC, but I think it really is that simple! 😄 |
Okay! Glad we're on the same page. :) I'm on-call for DevTools this week so I don't really have the bandwidth to dig in any further, but I think we have enough info now for this to be discussed/decided. I'm not positive but I don't think what you're looking for is possible (reading the |
You can function BadGuy() {
throw Error("sorry");
}
Object.defineProperty(BadGuy, "name", { value: "BadGuyDisplayName" }); |
Oh that's a nice point, Dan. I had forgotten about that. |
Using |
Interesting! I can confirm that it works on Chromium ( |
Just to be clear, |
PR #22477 may fix the issue being reported (without requiring the |
Looks like this issue has been inactive for awhile - if you'd prefer I open a new issue, let me know. The following issue still exists:
From reading the underlying React Source code, this behavior appears to be intentional, with the idea being native component stacks will give you the most accurate information for debugging. I agree with this to a point, but as noted earlier in the thread, this is very problematic for Higher Order Components which want to preserve the name of the component they're wrapping. In fact, this is such a primary use case that it is covered specifically in the React docs: https://reactjs.org/docs/higher-order-components.html#convention-wrap-the-display-name-for-easy-debugging As it stands currently, that documentation is not accurate, because defining This is all independent of alternate mechanisms such as the previously mentioned Accordingly, I would at least petition to drop "in Firefox" from this issue title, as the My personal suggestion is to override a portion of the stack frame with the
I am not certain if this is robust enough for general use, but it solved the problem for me and is at least a step in the right direction. |
Given the age of this issue, it seems like it won't be fixed any time soon. However, I did find a workaround! To recap, the problem is that given a snippet like this: function higherOrderComponent(wrapped) {
const wrappedComponent = (props) => {
return wrapped(props);
};
return wrappedComponent
} the closure will simply be named So we need a way to set the actual name (and not just a shadowed "name" property like the hack for Chrome) of our closure to whatever const wrappedComponent = { [wrapped.name](props) {
return wrapped(props);
} }[wrapped.name]; Also works for class components: const WrappedClassComp = { [Wrapped.name]: class extends Wrapped {
// ...
} }[Wrapped.name]; Implemented in react-easy-state here: RisingStack/react-easy-state@458e4bb |
It would be great to get React-16-like support for The |
Edit See this comment for the key part of what's being reported/discussed on this issue: #22315 (comment)
Original bug report below
As of #18561 component stacks are generated from native stack frames. This is problematic with HOCs that inherit from the input component in order to change its behavior. The somewhat popular @risingstack/react-easy-state package is one example of such a component. While it does assign a
displayName
, the new Native Component Stacks appear to ignore this. Instead, components wrapped inview()
(from react-easy-state) are always shown with the name of the wrapper class, i.e.,ReactiveComp
orReactiveClassComp
.This is especially catastrophic in the case of react-easy-state, where one is supposed to wrap essentially all components in the entire codebase in the
view()
HOC. The result is that component stacks become unusable for debugging.Is there perhaps a way to work around this (e.g. disable native component stacks, or some new way to explicitly provide a component name like
displayName
)?React version: 17.0.1
Steps To Reproduce
ReactiveComp
orReactiveClassComp
in component stack traces.Link to code example: https://codesandbox.io/s/rough-tdd-wqepe?file=/src/App.tsx
The current behavior
Note how the component wrapped in
view()
is shown asReactiveComp
instead of either the function name or the explicitly assigneddisplayName
.The expected behavior
The name of the
ReactiveComp
wrapper should never appear in component stacks.The text was updated successfully, but these errors were encountered: