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

Disable console.logs in the second render pass of DEV mode double render #18547

Merged
merged 3 commits into from Apr 8, 2020

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Apr 8, 2020

This disables console.log by temporarily patching the global console object during the second render pass when we double render in strict mode in DEV.

I could go either way on this. It is confusing that it double logs warnings, React, legacy react packages or third-party ones. However, if you do have a problem due to double rendering then you kind of want logs to debug it.

I also uses this override to detect this in our Scheduler.yieldValue helper so that it automatically doesn't double log which makes it easier to write tests that test render phases. This can be done without any internals being exposed so it works to test our build outputs.

The main motivation of this is actually a different PR where I want to double render to expose stack traces. In this case it's extra strange because it's extracting the error stack from the first one that causes new warnings that are not necessarily the same. So I think we need something there. We don't necessarily have to do the same for these ones.

This is ofc not exhaustive since there are so many other side-effects than just logs and you could have other APIs that show logs in UI etc.

@facebook-github-bot facebook-github-bot added React Core Team Opened by a member of the React Core Team CLA Signed labels Apr 8, 2020
@gaearon
Copy link
Collaborator

gaearon commented Apr 8, 2020

I was just debugging some internals yesterday and wished we had this.

@codesandbox-ci
Copy link

codesandbox-ci bot commented Apr 8, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit b7e2345:

Sandbox Source
competent-lake-s920v Configuration

Copy link
Collaborator

@acdlite acdlite left a comment

Choose a reason for hiding this comment

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

I only skimmed it

@acdlite
Copy link
Collaborator

acdlite commented Apr 8, 2020

Could we do the same for replayFailedUnitOfWorkWithInvokeGuardedCallback?

@@ -149,8 +148,7 @@ describe('ReactDOMFiberAsync', () => {
describe('concurrent mode', () => {
beforeEach(() => {
jest.resetModules();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can remove internal suffix now

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yea I'll do a follow up to rename all these if I land this. I think the remaining ones are just using the let ops = [] pattern and we can port those.

@sizebot
Copy link

sizebot commented Apr 8, 2020

Details of bundled changes.

Comparing: b225d4f...b7e2345

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js +0.3% +0.3% 630.4 KB 632.6 KB 132.89 KB 133.29 KB UMD_DEV
react-art.production.min.js 0.0% 0.0% 107.13 KB 107.13 KB 32.46 KB 32.46 KB UMD_PROD
react-art.development.js +0.4% +0.4% 534.92 KB 536.98 KB 115.3 KB 115.72 KB NODE_DEV
react-art.production.min.js 0.0% 0.0% 72.15 KB 72.15 KB 21.66 KB 21.67 KB NODE_PROD
ReactART-dev.js +0.4% +0.3% 596.51 KB 598.69 KB 125.04 KB 125.46 KB FB_WWW_DEV
ReactART-prod.js 0.0% 0.0% 248.21 KB 248.21 KB 41.94 KB 41.94 KB FB_WWW_PROD

scheduler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
scheduler-unstable_mock.development.js +2.4% +2.3% 21.82 KB 22.33 KB 5.22 KB 5.34 KB NODE_DEV
scheduler-unstable_mock.production.min.js 🔺+1.5% 🔺+1.4% 4.65 KB 4.72 KB 1.88 KB 1.9 KB NODE_PROD
SchedulerMock-dev.js +2.3% +2.3% 22.73 KB 23.25 KB 5.36 KB 5.49 KB FB_WWW_DEV
SchedulerMock-prod.js 🔺+0.9% 🔺+1.4% 11.9 KB 12 KB 2.79 KB 2.83 KB FB_WWW_PROD
scheduler-unstable_mock.development.js +2.3% +2.4% 23.41 KB 23.95 KB 5.34 KB 5.46 KB UMD_DEV
scheduler-unstable_mock.production.min.js 🔺+1.5% 🔺+1.3% 4.67 KB 4.74 KB 1.95 KB 1.98 KB UMD_PROD

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.production.min.js 0.0% 0.0% 119.69 KB 119.69 KB 37.52 KB 37.52 KB NODE_PROD
react-dom-test-utils.development.js 0.0% 0.0% 75.12 KB 75.12 KB 20.11 KB 20.11 KB UMD_DEV
react-dom-unstable-fizz.browser.development.js 0.0% -0.1% 4.9 KB 4.9 KB 1.64 KB 1.64 KB UMD_DEV
react-dom.profiling.min.js 0.0% 0.0% 123.54 KB 123.54 KB 38.66 KB 38.66 KB NODE_PROFILING
react-dom-server.node.production.min.js 0.0% -0.0% 23.23 KB 23.23 KB 8.65 KB 8.65 KB NODE_PROD
ReactDOMForked-dev.js +0.2% +0.2% 1005.25 KB 1007.43 KB 222.69 KB 223.11 KB FB_WWW_DEV
react-dom-unstable-fizz.node.development.js 0.0% +0.1% 5.16 KB 5.16 KB 1.69 KB 1.69 KB NODE_DEV
react-dom.development.js +0.2% +0.2% 903.52 KB 905.73 KB 198.37 KB 198.78 KB UMD_DEV
react-dom-server.browser.development.js 0.0% 0.0% 155 KB 155 KB 39.38 KB 39.38 KB UMD_DEV
react-dom.production.min.js 0.0% 0.0% 119.55 KB 119.55 KB 38.25 KB 38.25 KB UMD_PROD
react-dom.profiling.min.js 0.0% 0.0% 123.27 KB 123.27 KB 39.48 KB 39.48 KB UMD_PROFILING
react-dom.development.js +0.2% +0.2% 860.16 KB 862.22 KB 195.9 KB 196.31 KB NODE_DEV
react-dom-server.browser.development.js 0.0% 0.0% 145.85 KB 145.85 KB 38.63 KB 38.63 KB NODE_DEV
ReactDOM-dev.js +0.2% +0.2% 1005.25 KB 1007.43 KB 222.69 KB 223.11 KB FB_WWW_DEV
ReactDOMServer-dev.js 0.0% 0.0% 157.98 KB 157.98 KB 39.92 KB 39.92 KB FB_WWW_DEV
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 56.1 KB 56.1 KB 13.84 KB 13.84 KB UMD_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% 0.0% 9.98 KB 9.98 KB 3.36 KB 3.36 KB UMD_PROD
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 53.2 KB 53.2 KB 13.64 KB 13.64 KB NODE_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% 0.0% 9.73 KB 9.73 KB 3.25 KB 3.25 KB NODE_PROD

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler.development.js +0.4% +0.4% 574.28 KB 576.34 KB 121.48 KB 121.91 KB NODE_DEV
react-reconciler-reflection.production.min.js 0.0% 🔺+0.1% 2.57 KB 2.57 KB 1.08 KB 1.09 KB NODE_PROD

Size changes (stable)

Generated by 🚫 dangerJS against b7e2345

@sizebot
Copy link

sizebot commented Apr 8, 2020

Details of bundled changes.

Comparing: b225d4f...b7e2345

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js +0.4% +0.3% 650.67 KB 652.97 KB 136.59 KB 136.99 KB UMD_DEV
react-art.production.min.js 0.0% 0.0% 109.74 KB 109.74 KB 33.13 KB 33.13 KB UMD_PROD
react-art.development.js +0.4% +0.3% 554.4 KB 556.54 KB 119.07 KB 119.47 KB NODE_DEV
react-art.production.min.js 0.0% 0.0% 74.71 KB 74.71 KB 22.33 KB 22.33 KB NODE_PROD
ReactART-dev.js +0.4% +0.3% 586.45 KB 588.62 KB 123.06 KB 123.47 KB FB_WWW_DEV

scheduler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
scheduler-unstable_mock.development.js +2.3% +2.4% 23.42 KB 23.96 KB 5.34 KB 5.47 KB UMD_DEV
scheduler-unstable_mock.production.min.js 🔺+1.5% 🔺+1.3% 4.69 KB 4.76 KB 1.96 KB 1.99 KB UMD_PROD
scheduler-tracing.development.js 0.0% +0.1% 8.81 KB 8.81 KB 1.79 KB 1.79 KB NODE_DEV
scheduler-unstable_mock.development.js +2.4% +2.4% 21.83 KB 22.34 KB 5.23 KB 5.35 KB NODE_DEV
scheduler-unstable_mock.production.min.js 🔺+1.5% 🔺+1.3% 4.66 KB 4.73 KB 1.89 KB 1.91 KB NODE_PROD
SchedulerMock-dev.js +2.3% +2.3% 22.73 KB 23.25 KB 5.36 KB 5.49 KB FB_WWW_DEV
SchedulerMock-prod.js 🔺+0.9% 🔺+1.4% 11.9 KB 12 KB 2.79 KB 2.83 KB FB_WWW_PROD
scheduler.development.js 0.0% 0.0% 23.23 KB 23.23 KB 6.02 KB 6.02 KB NODE_DEV
scheduler.production.min.js 0.0% -0.0% 4.94 KB 4.94 KB 1.98 KB 1.98 KB NODE_PROD

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom-server.browser.production.min.js 0.0% 0.0% 23.39 KB 23.39 KB 8.6 KB 8.6 KB UMD_PROD
react-dom.profiling.min.js 0.0% 0.0% 127.81 KB 127.81 KB 39.89 KB 39.89 KB NODE_PROFILING
ReactDOM-dev.js +0.2% +0.2% 977.37 KB 979.55 KB 216.7 KB 217.13 KB FB_WWW_DEV
react-dom-unstable-fizz.browser.development.js 0.0% -0.1% 4.43 KB 4.43 KB 1.55 KB 1.55 KB NODE_DEV
react-dom-test-utils.development.js 0.0% 0.0% 75.14 KB 75.14 KB 20.11 KB 20.12 KB UMD_DEV
ReactDOMTesting-prod.js 0.0% 0.0% 393.2 KB 393.2 KB 71.65 KB 71.65 KB FB_WWW_PROD
ReactDOMTesting-profiling.js 0.0% 0.0% 393.2 KB 393.2 KB 71.65 KB 71.65 KB FB_WWW_PROFILING
react-dom-server.browser.development.js 0.0% 0.0% 147.36 KB 147.36 KB 38.84 KB 38.84 KB NODE_DEV
react-dom.development.js +0.2% +0.2% 933.35 KB 935.65 KB 203.65 KB 204.05 KB UMD_DEV
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 56.11 KB 56.11 KB 13.85 KB 13.85 KB UMD_DEV
react-dom.production.min.js 0.0% 0.0% 123.63 KB 123.63 KB 39.42 KB 39.42 KB UMD_PROD
ReactDOMForked-dev.js +0.2% +0.2% 977.37 KB 979.55 KB 216.7 KB 217.13 KB FB_WWW_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% 0.0% 10 KB 10 KB 3.36 KB 3.36 KB UMD_PROD
ReactDOMServer-dev.js 0.0% 0.0% 154.48 KB 154.48 KB 38.97 KB 38.97 KB FB_WWW_DEV
react-dom.profiling.min.js 0.0% 0.0% 127.47 KB 127.47 KB 40.63 KB 40.63 KB UMD_PROFILING
react-dom.development.js +0.2% +0.2% 888.75 KB 890.89 KB 201.17 KB 201.58 KB NODE_DEV
react-dom-unstable-native-dependencies.development.js 0.0% 0.0% 53.21 KB 53.21 KB 13.65 KB 13.65 KB NODE_DEV
react-dom.production.min.js 0.0% 0.0% 123.83 KB 123.83 KB 38.62 KB 38.62 KB NODE_PROD

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactNativeRenderer-prod.js 0.0% -0.0% 270.91 KB 270.91 KB 46.57 KB 46.57 KB RN_OSS_PROD
ReactFabric-prod.js 0.0% 0.0% 262.89 KB 262.89 KB 45.05 KB 45.05 KB RN_OSS_PROD
ReactFabric-dev.js +0.3% +0.3% 637.88 KB 639.96 KB 136.21 KB 136.64 KB RN_FB_DEV
ReactNativeRenderer-dev.js 0.0% 0.0% 653.75 KB 653.75 KB 140.33 KB 140.33 KB RN_OSS_DEV
ReactFabric-profiling.js 0.0% 0.0% 274.42 KB 274.42 KB 47.25 KB 47.25 KB RN_FB_PROFILING
ReactNativeRenderer-dev.js +0.3% +0.3% 656.16 KB 658.24 KB 140.65 KB 141.08 KB RN_FB_DEV
ReactFabric-dev.js 0.0% 0.0% 635.46 KB 635.46 KB 135.9 KB 135.9 KB RN_OSS_DEV

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler-reflection.production.min.js 0.0% 🔺+0.1% 2.58 KB 2.58 KB 1.09 KB 1.09 KB NODE_PROD
react-reconciler.development.js +0.4% +0.3% 596.12 KB 598.26 KB 125.76 KB 126.17 KB NODE_DEV

ReactDOM: size: 0.0%, gzip: 0.0%

Size changes (experimental)

Generated by 🚫 dangerJS against b7e2345

@sebmarkbage
Copy link
Collaborator Author

sebmarkbage commented Apr 8, 2020

Could we do the same for replayFailedUnitOfWorkWithInvokeGuardedCallback?

I think we probably could but that one was kind of so that you could break on the exception. The problem then is your logs are disabled while on your break point. That seems highly confusing. I'm not sure that's the right call.

);
disableLogs();
try {
value = renderWithHooks(
Copy link
Collaborator

Choose a reason for hiding this comment

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

I could foresee some confusion if the logs don't correspond to the value that is returned. It usually doesn't matter because it's supposed to be pure, but in the case where it's not, you might use console to debug it. So maybe they should be disabled for the first pass instead of the second.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So I was actually surprised that we used the second value. I was thinking we should ignore the second value like we do for class constructors.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In my other PR's replaying I have to ignore it because I force an error inside the function. Similarly the react-debug-tools for Hooks debugging it's just replayed stand-alone.

Doesn't necessarily mean we have to do the same here. I guess that's how I look at these.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's intentional because the children are bound to the work-in-progress fiber via the queue object via the dispatch method, and renderWithHooks mutates the fiber.

It was a very confusing bug when we found it :D

Copy link
Collaborator

Choose a reason for hiding this comment

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

This one: #14698

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 could go either way on if the second render should process the update queue and even leave any side-effects. :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Maybe the second render should have a different dispatcher that warns if you call setState again since you shouldn't call setState again for the same input.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I could go either way on if the second render should process the update queue and even leave any side-effects :)

It can work but it needs to include internal mutations/side-effects too. We'd have to make it so that renderWithHooks does not mutate any of the fiber fields. Or stash the result of the first pass (memoizedState, expirationTime, effectTag) before doing the second pass and then restore them.

Copy link
Collaborator

@acdlite acdlite Apr 8, 2020

Choose a reason for hiding this comment

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

Maybe the second render should have a different dispatcher that warns if you call setState again since you shouldn't call setState again for the same input.

Oh I didn't see that. Yeah I think that would work. Just call the component again with a special dispatcher that doesn't mutate anything.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe the second render should have a different dispatcher that warns if you call setState again since you shouldn't call setState again for the same input.

I might be misunderstanding this comment, but if the second call has state updates already applied, then we might miss out on triggering the observable side effects in the first place (e.g. like something that happens only when props/state meet a certain criteria).

@acdlite
Copy link
Collaborator

acdlite commented Apr 8, 2020

The problem then is your logs are disabled while on your break point

I was thinking you would disable the logs during the first pass and enable them for the second. In the case of an error, the second pass would use invokeGuardedCallback.

@sebmarkbage
Copy link
Collaborator Author

I was thinking you would disable the logs during the first pass and enable them for the second. In the case of an error, the second pass would use invokeGuardedCallback.

That would mean that we would have to always replay the second pass. At least as long as there's any log. Since most of the time when nothing throws or if it's just throwing a Promise, there wouldn't be a second pass.

@acdlite
Copy link
Collaborator

acdlite commented Apr 8, 2020

That would mean that we would have to always replay the second pass.

That seems fine? Isn't the only new case promises?

@sebmarkbage
Copy link
Collaborator Author

sebmarkbage commented Apr 8, 2020

That seems fine? Isn't the only new case promises?

No if we disable the logs we'd have to detect that we had silenced a log and replay - even if there's nothing thrown at all. So any little log would severely slow down debugging.

We can't replay the logs later because it would give them the wrong stacks frames in the devtools.

@acdlite
Copy link
Collaborator

acdlite commented Apr 8, 2020

I'm confused because we already replay the methods in dev. I don't get what's getting slower.

@sebmarkbage
Copy link
Collaborator Author

Hm. I was under the impression that we replay the whole render but I guess it's just the begin phase. Is that even safe from a resuming perspective? Seems like we'd have a lot of latent bugs there. Probably SuspenseList.

@sebmarkbage
Copy link
Collaborator Author

It seems like these two should just use the same mechanism. If the first pass errors, then we do the double render pass in invokeGuardedCallback

@sebmarkbage
Copy link
Collaborator Author

sebmarkbage commented Apr 8, 2020

We can do that in a follow up if we decide to land this. For now we need to decide on this (yes or no) to unblock the production stacks PR.

@sebmarkbage
Copy link
Collaborator Author

One fatal flaw of silencing the first pass is warning deduping. Almost none of our warnings would show up as it currently stands and also true if others use the same technique.

@sebmarkbage sebmarkbage changed the title [RFC] Disable console.logs in the second render pass of DEV mode double render Disable console.logs in the second render pass of DEV mode double render Apr 8, 2020
This was referenced Mar 15, 2021
@gaearon
Copy link
Collaborator

gaearon commented Mar 30, 2022

To follow up on this, we've changed the behavior in 18 to be more intuitive.
#21783 (comment)

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.

None yet