Skip to content
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

[Fizz] Implement Component Stacks in DEV for warnings #21610

Merged
merged 2 commits into from
Jun 3, 2021

Conversation

sebmarkbage
Copy link
Collaborator

This uses a reverse linked list in DEV-only to keep track of where we're currently executing. It works similar to context in that it saves these on the running task.

In addition to this we need access to the current task to extract it. We could keep it only the "current component stack" but we still need to be able to stash it on the task occasionally.

This causes a bit of an awkwardness where current task is available and also the task is passed as an argument to all current functions.

Note that I wasn't planning on using this for production stacks. We don't invoke error boundaries anyway but we could pass stacks to the global callback. But this seems a bit heavy just for that. A variant of this would be to use the plain stack but that doesn't carry along the stack between tasks. A compromise could be to only collect the stack on the current running stack. This is effectively what happens with Promises in production since async boundaries aren't tracked there.

@sebmarkbage sebmarkbage requested review from bvaughn and gaearon June 3, 2021 05:50
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Jun 3, 2021
@sizebot
Copy link

sizebot commented Jun 3, 2021

Comparing: 44cdfd6...3d2a1ab

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 127.29 kB 127.29 kB = 40.81 kB 40.81 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 130.11 kB 130.11 kB = 41.75 kB 41.75 kB
facebook-www/ReactDOM-prod.classic.js = 406.00 kB 406.00 kB = 75.07 kB 75.07 kB
facebook-www/ReactDOM-prod.modern.js = 394.35 kB 394.35 kB = 73.25 kB 73.25 kB
facebook-www/ReactDOMForked-prod.classic.js = 406.00 kB 406.00 kB = 75.07 kB 75.07 kB
facebook-www/ReactDOMServer-dev.modern.js +5.74% 203.36 kB 215.04 kB +5.90% 47.72 kB 50.54 kB
oss-stable/react-server/cjs/react-server.development.js +3.30% 110.96 kB 114.62 kB +2.67% 27.53 kB 28.26 kB
oss-experimental/react-server/cjs/react-server.development.js +3.29% 111.52 kB 115.18 kB +2.64% 27.68 kB 28.41 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
facebook-www/ReactDOMServer-dev.modern.js +5.74% 203.36 kB 215.04 kB +5.90% 47.72 kB 50.54 kB
oss-stable/react-server/cjs/react-server.development.js +3.30% 110.96 kB 114.62 kB +2.67% 27.53 kB 28.26 kB
oss-experimental/react-server/cjs/react-server.development.js +3.29% 111.52 kB 115.18 kB +2.64% 27.68 kB 28.41 kB
oss-stable/react-dom/umd/react-dom-unstable-fizz.browser.development.js +1.79% 219.70 kB 223.63 kB +1.50% 50.87 kB 51.63 kB
oss-experimental/react-dom/umd/react-dom-unstable-fizz.browser.development.js +1.78% 220.31 kB 224.24 kB +1.47% 51.03 kB 51.77 kB
oss-stable/react-dom/cjs/react-dom-unstable-fizz.node.development.js +1.75% 209.07 kB 212.73 kB +1.45% 50.21 kB 50.93 kB
oss-stable/react-dom/cjs/react-dom-unstable-fizz.browser.development.js +1.75% 209.12 kB 212.78 kB +1.45% 50.31 kB 51.04 kB
oss-experimental/react-dom/cjs/react-dom-unstable-fizz.node.development.js +1.75% 209.63 kB 213.29 kB +1.44% 50.36 kB 51.08 kB
oss-experimental/react-dom/cjs/react-dom-unstable-fizz.browser.development.js +1.75% 209.68 kB 213.34 kB +1.44% 50.46 kB 51.19 kB

Generated by 🚫 dangerJS against 3d2a1ab

}
function popComponentStackInDEV(task: Task): void {
if (__DEV__) {
invariant(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dev invariant. Meh?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rationalized it because it would be a React bug if it ever happened, so it shouldn't happen. But yea it shouldn't happen conditionally even then. I'll change it.

};
}: any);
if (__DEV__) {
task.componentStack = currentTaskInDEV ? task.componentStack : null;
Copy link
Contributor

@bvaughn bvaughn Jun 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you check currentTaskInDEV before reading from task?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a mistake. You're assigning task.componentStack = task.componentStack

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. Yea. Looks like I should add a test that tests component stacks when there's a suspended boundary inbetween.

Copy link
Contributor

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might need to add something similar to flight server renderer, for DevTools

@sebmarkbage
Copy link
Collaborator Author

The Flight server renderer is more complicated because it uses JSON.stringify so we don't have access to what happens on the stack. We'd have to reimplement that for DEV or perhaps which technique in both PROD/DEV. There are context related reasons we might want to switch implementation away from JSON.stringify for that reason.

@bvaughn
Copy link
Contributor

bvaughn commented Jun 3, 2021

The Flight server renderer is more complicated

Right 😬 but we will need something similar for DevTools. Right now, Flight server doesn't track either owner or parent.

This uses a reverse linked list in DEV-only to keep track of where we're
currently executing.
This makes it more explicit which stack we pass in to be retained by the
task.
);
}

// We can't use the toErrorDev helper here because this is an async act.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is fun. We can probably port it to make it work with async functions too.

@sebmarkbage sebmarkbage merged commit 1b7b359 into facebook:master Jun 3, 2021
facebook-github-bot pushed a commit to facebook/react-native that referenced this pull request Jun 7, 2021
Summary:
- **[0eea57724](facebook/react@0eea57724 )**: Fix typo in comment (accumlated → accumulated) ([#21637](facebook/react#21637)) //<ithinker5>//
- **[0706162ba](facebook/react@0706162ba )**: Fix typo in comment (environement → environment) ([#21635](facebook/react#21635)) //<niexq>//
- **[9d17b562b](facebook/react@9d17b562b )**: Fix typo in comment (satsify → satisfy) ([#21629](facebook/react#21629)) //<niexq>//
- **[b610fec00](facebook/react@b610fec00 )**: fix comments: expiration time -> lanes ([#21551](facebook/react#21551)) //<Shannon Feng>//
- **[cc4d24ab0](facebook/react@cc4d24ab0 )**: [Fizz] Always call flush() if it exists ([#21625](facebook/react#21625)) //<Dan Abramov>//
- **[e0d9b2899](facebook/react@e0d9b2899 )**: [Fizz] Minor Fixes for Warning Parity ([#21618](facebook/react#21618)) //<Sebastian Markbåge>//
- **[1b7b3592f](facebook/react@1b7b3592f )**: [Fizz] Implement Component Stacks in DEV for warnings ([#21610](facebook/react#21610)) //<Sebastian Markbåge>//
- **[39f007489](facebook/react@39f007489 )**: Make enableSuspenseLayoutEffectSemantics static for www ([#21617](facebook/react#21617)) //<Brian Vaughn>//
- **[8f3794276](facebook/react@8f3794276 )**: Prepare semver (`latest`) releases in CI ([#21615](facebook/react#21615)) //<Andrew Clark>//
- **[8b4201535](facebook/react@8b4201535 )**: Devtools: add feature to trigger an error boundary ([#21583](facebook/react#21583)) //<Bao Pham>//
- **[154a8cf32](facebook/react@154a8cf32 )**: Fix reference to wrong variable //<Andrew Clark>//
- **[6736a38b9](facebook/react@6736a38b9 )**: Add single source of truth for package versions ([#21608](facebook/react#21608)) //<Andrew Clark>//
- **[86715efa2](facebook/react@86715efa2 )**: Resolve the true entry point during tests ([#21505](facebook/react#21505)) //<Sebastian Markbåge>//
- **[a8a4742f1](facebook/react@a8a4742f1 )**: Convert ES6/TypeScript/CoffeeScript Tests to createRoot + act ([#21598](facebook/react#21598)) //<Sebastian Markbåge>//
- **[1d3558965](facebook/react@1d3558965 )**: Disable deferRenderPhaseUpdateToNextBatch by default ([#21605](facebook/react#21605)) //<Sebastian Markbåge>//
- **[a8964649b](facebook/react@a8964649b )**: Delete an unused field ([#21415](facebook/react#21415)) //<okmttdhr>//
- **[76f85b3e5](facebook/react@76f85b3e5 )**: Expose Fizz in stable builds ([#21602](facebook/react#21602)) //<Dan Abramov>//
- **[e16d61c30](facebook/react@e16d61c30 )**: [Offscreen] Mount/unmount layout effects ([#21386](facebook/react#21386)) //<Andrew Clark>//
- **[63091939b](facebook/react@63091939b )**: OSS feature flag updates ([#21597](facebook/react#21597)) //<Brian Vaughn>//
- **[efbd69b27](facebook/react@efbd69b27 )**:  Define global __WWW__ = true flag during www tests ([#21504](facebook/react#21504)) //<Sebastian Markbåge>//
- **[28625c6f4](facebook/react@28625c6f4 )**: Disable strict effects for legacy roots (again) ([#21591](facebook/react#21591)) //<Brian Vaughn>//
- **[3c2341416](facebook/react@3c2341416 )**: Update jest to v26 ([#21574](facebook/react#21574)) //<Sebastian Silbermann>//
- **[0d493dcda](facebook/react@0d493dcda )**: Removed _debugID field from Fiber - Issue #21558 ([#21570](facebook/react#21570)) //<Pulkit Sharma>//
- **[7841d0695](facebook/react@7841d0695 )**: Enable the updater-tracking feature flag in more builds ([#21567](facebook/react#21567)) //<Brian Vaughn>//
- **[6405efc36](facebook/react@6405efc36 )**: Enabled Profiling feature flags for OSS release ([#21565](facebook/react#21565)) //<Brian Vaughn>//

Changelog:
[General][Changed] - React Native sync for revisions 2d8d133...0eea577

jest_e2e[run_all_tests]

Reviewed By: bvaughn

Differential Revision: D28932083

fbshipit-source-id: 012c1bfb857ed59d7283334d633f1cce8ec50360
koto pushed a commit to koto/react that referenced this pull request Jun 15, 2021
* Implement component stacks

This uses a reverse linked list in DEV-only to keep track of where we're
currently executing.

* Fix bug that wasn't picking up the right stack at suspended boundaries

This makes it more explicit which stack we pass in to be retained by the
task.
sebmarkbage added a commit that referenced this pull request Jul 4, 2024
…ing onError/onPostpone (#30206)

Stacked on #30197.

This is similar to #30182 and #21610 in Fizz.

Track the current owner/stack/task on the task. This tracks it for
attribution when serializing child properties.

This lets us provide the right owner and createTask when we
console.error from inside Flight itself. This also affects the way we
print those logs on the client since we need the owner and stack. Now
console.errors that originate on the server gets the right stack on the
client:

<img width="760" alt="Screenshot 2024-07-03 at 6 03 13 PM"
src="https://github.com/facebook/react/assets/63648/913300f8-f364-4e66-a19d-362e8d776c64">

Unfortunately, because we don't track the stack we never pop it so it'll
keep tracking for serializing sibling properties. We rely on "children"
typically being the last property in the common case anyway. However,
this can lead to wrong attribution in some cases where the invalid
property is a next property (without a wrapping element) and there's a
previous element that doesn't. E.g. `<ClientComponent title={<div />}
invalid={nonSerializable} />` would use the div as the attribution
instead of ClientComponent.

I also wrap all of our own console.error, onError and onPostpone in the
context of the parent component. It's annoying to have to remember to do
this though.

We could always wrap the whole rendering in such as context but it would
add more overhead since this rarely actually happens. It might make
sense to track the whole current task instead to lower the overhead.
That's what we do in Fizz. We'd still have to remember to restore the
debug task though. I realize now Fizz doesn't do that neither so the
debug task isn't wrapping the console.errors that Fizz itself logs.
There's something off about that Flight and Fizz implementations don't
perfectly align.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants