profile
viewpoint
Brian Vaughn bvaughn @facebook Mountain View, CA http://www.briandavidvaughn.com React JS core team at @facebook; formerly at @treasure-data and @google.

bvaughn/angular-form-for 435

Set of Angular directives to simplify creating and validating HTML forms.

bvaughn/debounce-decorator 75

Decorator for debouncing class methods

bvaughn/babel-repl 36

React powered Babel REPL

bvaughn/extensions-api-proposal-custom-performance-pane 35

Extensions API proposal for the Performance Panel

bvaughn/console.pretty 25

Pretty console logging

bvaughn/connect-tech-2016 12

Connect Tech 2016 presentation

bvaughn/faux-codesandbox-client 8

Example Code Sandbox and React DevTools v4 integration

bvaughn/flat-object 6

Utilities for working with nested data structures

bvaughn/bootstrap-select-button 2

Creates an Angular-friendly bindable button dropdown using the Bootstrap CSS framework

bvaughn/cron-parser 1

Node.js library for parsing crontab instructions

issue closedfacebook/react

Error: "Commit tree does not contain fiber 118764. This is a bug in React DevTools."

Describe what you were doing when the bug occurred:

I was profiling unmounting of 30k+ records and this error occurred in dev tools


Please do not remove the text below this line

DevTools version: 4.8.2-fed4ae024

Call stack: at updateTree (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:17854:21) at getCommitTree (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:17697:24) at ProfilingCache.getCommitTree (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:18265:14) at CommitFlamegraphAutoSizer (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:31718:33) at vh (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:11067:7) at fi (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:11733:7) at ck (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:14430:86) at bk (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:13779:11) at ak (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:13768:5) at Sj (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:13750:7)

Component stack: at CommitFlamegraphAutoSizer (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:31701:48) at div at div at div at SettingsModalContextController (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:26139:23) at Profiler_Profiler (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:33363:48) at ErrorBoundary (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:27172:5) at PortaledContent (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:27303:32) at div at div at ProfilerContextController (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:30463:23) at TreeContextController (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:22538:23) at SettingsContextController (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:23040:27) at ModalDialogContextController (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:28328:23) at DevTools_DevTools (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:33797:21)

closed time in 9 hours

FatehAK

issue commentfacebook/react

Error: "Commit tree does not contain fiber 118764. This is a bug in React DevTools."

I'm sorry you experienced this bug! 🙇

Looks like this problem was already reported though (#18859) so I'm going to mark this issue as a duplicate and close it. If you have additional context that might help us repro this problem, please leave a comment on the other issue!

Thank you~

FatehAK

comment created time in 9 hours

Pull request review commentfacebook/react

Double Invoke Effects in __DEV__

+/**+ * Copyright (c) Facebook, Inc. and its affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ *+ * @emails react-core+ */++'use strict';++let React;+let ReactFeatureFlags;+let ReactNoop;+let Scheduler;++describe('ReactDoubleInvokeEvents', () => {+  beforeEach(() => {+    jest.resetModules();+    React = require('react');+    ReactFeatureFlags = require('shared/ReactFeatureFlags');+    ReactNoop = require('react-noop-renderer');+    Scheduler = require('scheduler');+    ReactFeatureFlags.enableDoubleInvokingEffects = true;+  });++  it('double invoking for effects works properly', () => {+    function App({text}) {+      React.useEffect(() => {+        Scheduler.unstable_yieldValue('useEffect mount');+        return () => Scheduler.unstable_yieldValue('useEffect unmount');+      });++      React.useLayoutEffect(() => {+        Scheduler.unstable_yieldValue('useLayoutEffect mount');+        return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');+      });++      return text;+    }+    ReactNoop.act(() => {+      ReactNoop.render(<App text={'mount'} />);+    });++    if (__DEV__) {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect mount',+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useEffect mount',+        'useEffect unmount',+        'useEffect mount',+      ]);+    } else {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect mount',+        'useEffect mount',+      ]);+    }++    ReactNoop.act(() => {+      ReactNoop.render(<App text={'update'} />);+    });++    if (__DEV__) {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useEffect unmount',+        'useEffect mount',+        'useEffect unmount',+        'useEffect mount',+      ]);+    } else {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useEffect unmount',+        'useEffect mount',+      ]);+    }+  });+  it('first useEffect double invokes before second useEffect is called', () => {+    function App({text}) {+      React.useEffect(() => {+        Scheduler.unstable_yieldValue('useEffect One mount');+        return () => Scheduler.unstable_yieldValue('useEffect One unmount');+      });++      React.useEffect(() => {+        Scheduler.unstable_yieldValue('useEffect Two mount');+        return () => Scheduler.unstable_yieldValue('useEffect Two unmount');+      });++      return text;+    }++    ReactNoop.act(() => {+      ReactNoop.render(<App text={'mount'} />);+    });++    if (__DEV__) {+      expect(Scheduler).toHaveYielded([+        'useEffect One mount',+        'useEffect One unmount',+        'useEffect One mount',+        'useEffect Two mount',+        'useEffect Two unmount',+        'useEffect Two mount',+      ]);+    } else {+      expect(Scheduler).toHaveYielded([+        'useEffect One mount',+        'useEffect Two mount',+      ]);+    }++    ReactNoop.act(() => {+      ReactNoop.render(<App text={'update'} />);+    });++    if (__DEV__) {+      expect(Scheduler).toHaveYielded([+        'useEffect One unmount',+        'useEffect Two unmount',+        'useEffect One mount',+        'useEffect One unmount',+        'useEffect One mount',+        'useEffect Two mount',+        'useEffect Two unmount',+        'useEffect Two mount',

Ah! Yeah, I think that makes sense.

Cool. Ping me again when this is ready for review (or we can talk about it or pair on it if you'd like too)

lunaruan

comment created time in 10 hours

Pull request review commentfacebook/react

Double Invoke Effects in __DEV__

+/**+ * Copyright (c) Facebook, Inc. and its affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ *+ * @emails react-core+ */++'use strict';++let React;+let ReactFeatureFlags;+let ReactNoop;+let Scheduler;++describe('ReactDoubleInvokeEvents', () => {+  beforeEach(() => {+    jest.resetModules();+    React = require('react');+    ReactFeatureFlags = require('shared/ReactFeatureFlags');+    ReactNoop = require('react-noop-renderer');+    Scheduler = require('scheduler');+    ReactFeatureFlags.enableDoubleInvokingEffects = true;+  });++  it('double invoking for effects works properly', () => {+    function App({text}) {+      React.useEffect(() => {+        Scheduler.unstable_yieldValue('useEffect mount');+        return () => Scheduler.unstable_yieldValue('useEffect unmount');+      });++      React.useLayoutEffect(() => {+        Scheduler.unstable_yieldValue('useLayoutEffect mount');+        return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');+      });++      return text;+    }+    ReactNoop.act(() => {+      ReactNoop.render(<App text={'mount'} />);+    });++    if (__DEV__) {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect mount',+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useEffect mount',+        'useEffect unmount',+        'useEffect mount',+      ]);+    } else {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect mount',+        'useEffect mount',+      ]);+    }++    ReactNoop.act(() => {+      ReactNoop.render(<App text={'update'} />);+    });++    if (__DEV__) {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useEffect unmount',+        'useEffect mount',+        'useEffect unmount',+        'useEffect mount',+      ]);+    } else {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useEffect unmount',+        'useEffect mount',+      ]);+    }+  });+  it('first useEffect double invokes before second useEffect is called', () => {+    function App({text}) {+      React.useEffect(() => {+        Scheduler.unstable_yieldValue('useEffect One mount');+        return () => Scheduler.unstable_yieldValue('useEffect One unmount');+      });++      React.useEffect(() => {+        Scheduler.unstable_yieldValue('useEffect Two mount');+        return () => Scheduler.unstable_yieldValue('useEffect Two unmount');+      });++      return text;+    }++    ReactNoop.act(() => {+      ReactNoop.render(<App text={'mount'} />);+    });++    if (__DEV__) {+      expect(Scheduler).toHaveYielded([+        'useEffect One mount',+        'useEffect One unmount',+        'useEffect One mount',+        'useEffect Two mount',+        'useEffect Two unmount',+        'useEffect Two mount',+      ]);+    } else {+      expect(Scheduler).toHaveYielded([+        'useEffect One mount',+        'useEffect Two mount',+      ]);+    }++    ReactNoop.act(() => {+      ReactNoop.render(<App text={'update'} />);+    });++    if (__DEV__) {+      expect(Scheduler).toHaveYielded([+        'useEffect One unmount',+        'useEffect Two unmount',+        'useEffect One mount',+        'useEffect One unmount',+        'useEffect One mount',+        'useEffect Two mount',+        'useEffect Two unmount',+        'useEffect Two mount',

I think this is actually a regression.

We guarantee all destroy functions run before any mount functions. This is important because interleaving destroy/create effects between sibling components might cause components to interfere with each other (e.g. a destroy function in one component may unintentionally override a ref value set by a create function in another component). This was something we recently fixed, via #17945.

The approach implemented in this PR seems to regress, since in DEV mode, when we double-invoke things, we break the order (as shown in this unit test).

I think we should rethink the approach here. Let's talk?

lunaruan

comment created time in 11 hours

Pull request review commentfacebook/react

Double Invoke Effects in __DEV__

+/**+ * Copyright (c) Facebook, Inc. and its affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ *+ * @emails react-core+ */++'use strict';++let React;+let ReactFeatureFlags;+let ReactNoop;+let Scheduler;++describe('ReactDoubleInvokeEvents', () => {+  beforeEach(() => {+    jest.resetModules();+    React = require('react');+    ReactFeatureFlags = require('shared/ReactFeatureFlags');+    ReactNoop = require('react-noop-renderer');+    Scheduler = require('scheduler');+    ReactFeatureFlags.enableDoubleInvokingEffects = true;+  });++  it('double invoking for effects works properly', () => {+    function App({text}) {+      React.useEffect(() => {+        Scheduler.unstable_yieldValue('useEffect mount');+        return () => Scheduler.unstable_yieldValue('useEffect unmount');+      });++      React.useLayoutEffect(() => {+        Scheduler.unstable_yieldValue('useLayoutEffect mount');+        return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');+      });++      return text;+    }+    ReactNoop.act(() => {+      ReactNoop.render(<App text={'mount'} />);+    });++    if (__DEV__) {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect mount',+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useEffect mount',+        'useEffect unmount',+        'useEffect mount',+      ]);+    } else {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect mount',+        'useEffect mount',+      ]);+    }++    ReactNoop.act(() => {+      ReactNoop.render(<App text={'update'} />);+    });++    if (__DEV__) {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useEffect unmount',+        'useEffect mount',+        'useEffect unmount',+        'useEffect mount',+      ]);+    } else {+      expect(Scheduler).toHaveYielded([+        'useLayoutEffect unmount',+        'useLayoutEffect mount',+        'useEffect unmount',+        'useEffect mount',+      ]);+    }

No unmount test? :smile:

lunaruan

comment created time in 11 hours

push eventfacebook/react

Halit Ogunc

commit sha 8d57ca519a3f8e7f76f81f404d048dfc35303d98

fix: typo in React Release Scripts (#19524)

view details

push time in 12 hours

pull request commentfacebook/react

Fix typo in React Release Scripts

Thanks

halitogunc

comment created time in 12 hours

PR merged facebook/react

Fix typo in React Release Scripts CLA Signed

The document had a small typo.

+1 -1

3 comments

1 changed file

halitogunc

pr closed time in 12 hours

issue closedfacebook/react

Bug:

<!-- Please provide a clear and concise description of what the bug is. Include screenshots if needed. Please test using the latest version of the relevant React packages to make sure your issue has not already been fixed. -->

React version:

Steps To Reproduce

<!-- Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Issues without reproduction steps or code examples may be immediately closed as not actionable. -->

Link to code example:

<!-- Please provide a CodeSandbox (https://codesandbox.io/s/new), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem. You may provide a screenshot of the application if you think it is relevant to your bug report. Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve. -->

The current behavior

The expected behavior

closed time in 12 hours

12345678kkmmxx

pull request commentfacebook/react

Fix: React cannot render in ShadowRoot

@trueadm is correct. React only overrides the console methods for the second render, which it only does in DEV. This prevents duplicate logs from being in the console and confusing people. We understand that it might be confusing as well to see a console.log statement that doesn't print anything– but we think that's a less common case.

Jack-Works

comment created time in 12 hours

Pull request review commentreactjs/reactjs.org

Adding a little feedback form to the bottom of each page using GA

+/**+ * Copyright (c) Facebook, Inc. and its affiliates.+ *+ * @emails react-core+ * @flow+ */++import React, {useState} from 'react';+import {trackCustomEvent} from 'gatsby-plugin-google-analytics';+import {sharedStyles} from 'theme';++const FeedbackForm = () => {+  const [feedbackGiven, setFeedbackGiven] = useState(false);++  if (feedbackGiven) {+    return 'Thanks for letting us know!';+  } else {+    return (+      <>+        Is this page useful?+        <button+          css={[sharedStyles.articleLayout.feedbackButton, {marginLeft: '6px'}]}+          aria-label="Yes"+          onClick={e => {+            e.preventDefault();+            trackCustomEvent({+              category: 'Feedback Button',+              action: 'feedback',+              label: window.location.pathname,+              value: 1,+            });

I don't know much about how our GA stuff works. I assumed these buttons are wired up correctly and have been confirmed to work? 😄

rachelnabors

comment created time in 14 hours

Pull request review commentreactjs/reactjs.org

Adding a little feedback form to the bottom of each page using GA

 import Container from 'components/Container'; import Flex from 'components/Flex'; import MarkdownHeader from 'components/MarkdownHeader'; import NavigationFooter from 'templates/components/NavigationFooter';-import React from 'react';+import React, {useState} from 'react';

Looks like an unnecessary new import.

rachelnabors

comment created time in 14 hours

issue commentfacebook/react

Bug: TypeScript: Passing a Component Constructor as prop does not adhere to prop's type

No problem! 😄

We don't actually maintain the React TypeScript definitions either 😅 so even those should be filed in the DefinitelyTyped repo.

NicoleRauch

comment created time in 2 days

issue closedfacebook/react

Bug: about warning when using the same context provider between multiple react reconcilers

I am currently developing a web app that uses both react-pixi and react-babylonjs. Both of these libraries use react-reconciler. I also use redux in my project, so they share the same Context in the two libraries.

It displays a warning on Console after every state updating, but everything works well, both reconcilers can trigger an update.

I want to know if there is any risk in doing this, or is this just a false warning?

React version: 16.13.1

Steps To Reproduce

  1. Using multiple react reconcilers
  2. Using the same context provider between that react reconcilers

Link to code example: https://codesandbox.io/s/multiple-reconciler-using-same-context-v8kq1?file=/src/App.js

The current behavior

It will throw a warning message after every state updating:

Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.

But everything works well, both reconcilers can trigger an update.

The expected behavior

Don't show any warning.

closed time in 2 days

vimcaw

issue commentfacebook/react

Bug: about warning when using the same context provider between multiple react reconcilers

React only supports two concurrent renderers at most– one "primary" and one "secondary", e.g. React DOM (primary) and React ART (secondary) or React Native (primary) and Fabric (secondary). This is partially a practical constraint (in that it covers 99% of use cases) and partially an intentional trade off in that certain APIs (like Context or useMutableSource) are able to be more efficiently implemented because of it.

For example, rather than using a (slower) Map structure to maintain a (per-renderer) stack of context values, the Context API is able to store this stack on the context object using a designated field: https://github.com/facebook/react/blob/93a0c2830534cfbc4e6be3ecc9c9fc34dee3cfaa/packages/react-reconciler/src/ReactFiberNewContext.new.js#L78-L111

Same for the useMutableSource API: https://github.com/facebook/react/blob/93a0c2830534cfbc4e6be3ecc9c9fc34dee3cfaa/packages/react-reconciler/src/ReactMutableSource.new.js#L30-L62

Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.

This warning suggests that two renderers of the same type (presumably two primary renderers) are both using a context at the same time (concurrently). This might happen to work okay in some cases (e.g. if only the global/default context value is being used) but it may also break depending on how each renderer is using the context.

Hope this helps answer your question! tl;dr is that the warning is valid and important.

vimcaw

comment created time in 2 days

issue closedfacebook/react

Bug: can't inspect well react warnings for `componentWillReceiveProps has been renamed`

Cf the screenshot bellow. I have no componentWillReceiveProps in my code, so the warning comes from a library. I don't know how to quickly identify which library causes this warning and make a fix pull request on the repo of this library.

Any tips to also remove the warning ?

Thanks a lot

image

closed time in 2 days

Aarbel

issue commentfacebook/react

Bug: can't inspect well react warnings for `componentWillReceiveProps has been renamed`

The error message shows the names of the components containing the old lifecycle method:

Please update the following components: Checkbox, Folder, GUI

Given those names, my guess is that it's a UI library like material-ui.

Unfortunately there is no way for us to detect and log the name of the packages. That metadata wouldn't be accessible to React at runtime. We could print full component stacks showing each place the problematic components are used, but that would make the warnings very long and clutter the console more than they already do, so we opted to just show the name of the components instead.

Hope this is hepful!

Aarbel

comment created time in 2 days

issue commentfacebook/react

Bug: TypeScript: Passing a Component Constructor as prop does not adhere to prop's type

Am I reading this wrong, or is the bug being reported here a TypeScript issue? React has nothing to do with TypeScript. If you believe you've found a TypeScript bug, you should file it in the TypeScript repo: https://github.com/microsoft/TypeScript

NicoleRauch

comment created time in 2 days

issue commentfacebook/react

Devtools tabs should not appear in chrome-extension pages when using react-devtools as an entry point

@nickmccurdy Realistically, I don't think anyone is going to work on this issue considering how unusual the use case is. If it's important to you, you might need to pick it up.

nickmccurdy

comment created time in 3 days

issue closedfacebook/react

Bug: requirement on matching SSR and CSR forces CSR to return null

React16 asks for exact matching between SSR and CSR for the first round of rendering. If I have partial content which depends on window.width, I need to force it to return null in the first render of client side hydration, resulting in bad performance.

Use case: say I have an UI widget contains the following structure

<div>
   <img src={imgSrc}
   {window && <video src={window.innerWidth > 500 ? desktopSrc : mobileSrc} />}
</div>

if it is done by purely client side rendering, everything is good. In the first render, I can show the right video to customer depending on window.innerWidth.

But lets say I want to support SSR for this widget. Because in server side the window is undefined, it will just return

<div>
  <img src={imgSrc}
</div>

My image is happily enjoying the preloading of browser.

In client side hydration, it will update it with the right video in the first render. Say if currently window.innerWidth < 500, it will return

<div>
  <img src={imgSrc}
  <video src={mobileVideo />
</div>

Not that bad since customers just need to wait for the first round of hydration.

But React does not like it, since there is a mismatch between SSR and CSR for their first render. The 'right' way to do it is

<div>
   <img src={imgSrc}
   {didMount && <video src={window.innerWidth > 500 ? desktopSrc : mobileSrc} />}
</div>

So that the first round of hydration matches SSR result (no video). But we are forcing customer to wait a little longer.

Then I question myself: what's the benefit of doing SSR for the content that cant be determined in server side? It seems purely client side render wins(at least we can see the right video on first CSR round), but...I still want to do SSR for the other contents.. Can I ask React to only do partially SSR for my UI? No...

closed time in 4 days

hkjpotato

issue commentfacebook/react

Bug: requirement on matching SSR and CSR forces CSR to return null

This is not a bug. It's just how the server-side rendering API works. Mismatched output on the server/client can indicate a bug, so React warns about it. (This warning happens in DEV only, and React does not actually do anything more than warn– it does not try to patch up the differences.)

As for the question of, "what's the benefit of doing SSR" – the majority of your app does not change in the scenario you describe, so the second render after client mount can be very fast. Meaning that the overall experience can still be better with SSR than without.

hkjpotato

comment created time in 4 days

pull request commentfacebook/react

Support untagged releases

Another case is patches, which are cut from a branch on top of the latest release. Those have no relationship to the next channel.

This seems like a reasonably compelling argument.

gaearon

comment created time in 4 days

Pull request review commentfacebook/react

Support untagged releases

+#!/usr/bin/env node++'use strict';++const {readJson} = require('fs-extra');+const {join} = require('path');+const theme = require('../theme');++const run = async ({cwd, packages, tag}) => {+  // Prevent a "next" release from ever being published as @latest+  // All canaries share a version number, so it's okay to check any of them.+  const arbitraryPackageName = packages[0];+  const packageJSONPath = join(+    cwd,+    'build',+    'node_modules',+    arbitraryPackageName,+    'package.json'+  );+  const {version} = await readJson(packageJSONPath);+  if (version.indexOf('0.0.0') === 0) {+    if (tag === 'latest') {+      if (version.indexOf('experimental') !== -1) {

Looks like you meant to use === here? (Or you meant to reverse the log messages below?)

Given the number of indexOf checks in this section, it might be clearer if you assigned that to a variable e.g. isExperimentalVersion.

gaearon

comment created time in 4 days

starteddandavison/delta

started time in 4 days

issue commentjeffkaufman/icdiff

Feature Request: Syntax Highlighting

I don't see how to keep the colors from diffing and the colors from syntax highlighting from interfering.

Totally respect your stance on not wanting to implement this. I imagine it could be a lot of work.

But I am curious if you considered using background color for diffing and foreground color for syntax highlighting (similar to what GitHub does)?

madnight

comment created time in 4 days

issue commentfacebook/react

Inspector doesn't work on my site

Since the issue is not currently actionable, I'm going to close this issue until more information is provided.

maxrothman

comment created time in 5 days

issue closedfacebook/react

Inspector doesn't work on my site

Do you want to request a feature or report a bug? Ask for assistance or report a bug (not sure)

Steps to reproduce

  • In dev tools, select "Components" tab
  • Select the inspector tool in the top-left
  • Click on an element in the page

What is the current behavior? Component names are shown as I mouse over them, but the Components tab does not reveal the selected element

What is the expected behavior? The selected element should be revealed in the Components tab

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React? React 16.8.6 Chrome 79.0.3945.79 React dev tools 4.2.1

The inspector feature works on other public websites built with React, so the problem almost certainly lies with my site. What am I doing wrong, or have I uncovered an edge case somewhere?

closed time in 5 days

maxrothman

issue closedfacebook/react

Error: "Cannot read property 'replace' of undefined"

Describe what you were doing when the bug occurred: 1. 2. 3.


Please do not remove the text below this line

DevTools version: 4.7.0-23309eb38

Call stack: at chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:313982 at Ii (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:314116) at gi (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:62450) at zl (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:112731) at Ic (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:104502) at Tc (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:104430) at Dc (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:104298) at vc (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:99542) at F (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:3011) at MessagePort.w.port1.onmessage (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:1767)

Component stack: at Ii (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:313542) at div at Oi (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:310221) at div at Ti (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:307361) at Suspense at pi (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:300435) at div at div at Uo (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:272479) at Hr (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:250828) at Co (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:263571) at chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:333079 at n (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:276314) at chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:278724 at div at div at Xi (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:325177) at Ge (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:207026) at sn (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:216342) at Va (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:293773) at us (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:371869)

closed time in 5 days

SunnyDark

issue commentfacebook/react

Error: "Cannot read property 'replace' of undefined"

Since the issue is not currently actionable, I'm going to close this issue until more information is provided.

SunnyDark

comment created time in 5 days

issue closedfacebook/react

Bug:[Jest Timer Mocks] act is diff with react-dom/utils make act warning on async act

When i use import { act } from 'react-test-rerender'; image

When i replace it with import { act } from 'react-dom/test-utils';

this warning is gone :v:

React version: react-test-renderer: 16.13.1 react-dom: 16.13.1 jest: 26.0.0 Code example:

await act(async () => {
  jest.runAllTimers();
});

closed time in 5 days

memsenpai

issue commentfacebook/react

Bug:[Jest Timer Mocks] act is diff with react-dom/utils make act warning on async act

Since the issue is not currently actionable, I'm going to close this issue until more information is provided.

memsenpai

comment created time in 5 days

issue commentfacebook/react

Bug: Object(...) is not a function

Since the issue is not currently actionable, I'm going to close this issue until more information is provided.

hieusmiths

comment created time in 5 days

issue closedfacebook/react

Bug: Object(...) is not a function

My React version:

"react": "^16.13.1"

The current behavior

The first i'am using React with version 16.8.6 and met a bug (Object(...) is not a function) In this component i'm using React.ContextAPI.

Then i updated React to version 16.13.1 and it not working.

Thank sou much React Team.

closed time in 5 days

hieusmiths

issue closedfacebook/react

Bug: React Dev Tool extension won't load for a development server

I'm not sure if screenshots are necessary in this case. Please ask if you need them. React Dev Tool extension won't load up when working locally. It loads up fine when on live server.

browser: Firefox Dev Edition OS: Ubuntu 20.04

Steps To Reproduce

  1. Install latest Firefox Dev Edition
  2. Add React Dev Tool extension
  3. Run local dev server (on Docker or off)

The current behavior

Described above

The expected behavior

The extension loading up no matter if working locally or in production.

closed time in 5 days

codinghedgehog-python

issue closedfacebook/react

Bug:

Warning: A future version of React will block javascript: URLs as a security precaution. Use event handlers instead if you can. If you need to generate unsafe HTML try using dangerouslySetInnerHTML instead. React was passed "javascript:;".

closed time in 5 days

dnyansons001

issue commentfacebook/react

Bug:

Since the issue is not currently actionable, I'm going to close this issue until more information is provided.

dnyansons001

comment created time in 5 days

issue closedfacebook/react

Error: "getCommitTree(): Unable to reconstruct tree for root "1" and commit 5"

Describe what you were doing when the bug occurred: 1. 2. 3.


Please do not remove the text below this line

DevTools version: 4.7.0-23309eb38

Call stack: at I (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:163895) at I (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:163773) at e.getCommitTree (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:166664) at Ul (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:342328) at gi (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:62450) at tl (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:71793) at zl (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:113765) at Ic (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:104502) at Tc (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:104430) at Dc (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:32:104298)

Component stack: at Ul (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:342099) at div at div at div at Co (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:263571) at chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:366677 at n (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:276314) at chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:278724 at div at div at Xi (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:325177) at Ge (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:207026) at sn (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:216342) at Va (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:293773) at us (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/main.js:40:371869)

closed time in 5 days

ajishvnair

issue commentfacebook/react

Error: "getCommitTree(): Unable to reconstruct tree for root "1" and commit 5"

Since the issue is not currently actionable, I'm going to close this issue until more information is provided.

ajishvnair

comment created time in 5 days

issue commentfacebook/react

Bug: React Dev Tool extension won't load for a development server

It doesn't look like this bug report has enough info for one of us to reproduce it.

Please provide a CodeSandbox (https://codesandbox.io/s/new), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem. Screenshots or videos can also be helpful if they help provide context on how to repro the bug.

Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve

Since the issue is not currently actionable, I'm going to close this issue until more information is provided.

codinghedgehog-python

comment created time in 5 days

push eventbvaughn/react

Andrew Clark

commit sha e1f96b82b4aff1d5c50d02c76df9c60e32dba408

Check PassiveStatic instead of Passive (#19489) Saves us from having to set a flag on `current` during the layout phase. Could result in some redundant traversal, since PassiveStatic includes effects that don't need clean-up. But it's worth it to remove the work from the layout phase. While I was editing this, I also re-arranged it so that we check the `effectTag` check before we check the `tag`, since the `effectTag` check is the one that's more likely to fail.

view details

Andrew Clark

commit sha 6ef997b534bfbf10071401ea15b39a678d557135

Check for passive effects on the root fiber (#19488) The root fiber doesn't have a parent from which we can read the `subtreeTag`, so we need to check its `effectTag` directly. The root fiber previously did not have any pending passive effects, but it does now that deleted fibers are cleaned up in the passive phase. This allows us to remove a `schedulePassiveEffectCallback` call from the synchronous unmount path. Co-authored-by: Brian Vaughn <bvaughn@fb.com>

view details

Brian Vaughn

commit sha 5864d157d9d20328bc34812468313375a49c0195

Moved resetChildLanes into complete work This enabled us to remove a few hot path tag-type checks, but did not otherwise change any functionality.

view details

push time in 6 days

PR closed facebook/react

Update README.md CLA Signed

<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory.

Before submitting a pull request, please make sure the following is done:

  1. Fork the repository and create your branch from master.
  2. Run yarn in the repository root.
  3. If you've fixed a bug or added code that should be tested, add tests!
  4. Ensure the test suite passes (yarn test). Tip: yarn test --watch TestName is helpful in development.
  5. Run yarn test-prod to test in the production environment. It supports the same options as yarn test.
  6. If you need a debugger, run yarn debug-test --watch TestName, open chrome://inspect, and press "Inspect".
  7. Format your code with prettier (yarn prettier).
  8. Make sure your code lints (yarn lint). Tip: yarn linc to only check changed files.
  9. Run the Flow type checks (yarn flow).
  10. If you haven't already, complete the CLA.

Learn more about contributing: https://reactjs.org/docs/how-to-contribute.html -->

Summary

<!-- Explain the motivation for making this change. What existing problem does the pull request solve? -->

Test Plan

<!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. -->

+2 -2

7 comments

1 changed file

Abiola-Farounbi

pr closed time in 6 days

pull request commentfacebook/react

Update README.md

Thank you for the PR suggestion, @Abiola-Farounbi!

I agree with Ricky though that these changes aren't necessarily improvements, so I'm going to close this PR for now.

Have a great day!

Abiola-Farounbi

comment created time in 6 days

push eventfacebook/react

Andrew Clark

commit sha 6ef997b534bfbf10071401ea15b39a678d557135

Check for passive effects on the root fiber (#19488) The root fiber doesn't have a parent from which we can read the `subtreeTag`, so we need to check its `effectTag` directly. The root fiber previously did not have any pending passive effects, but it does now that deleted fibers are cleaned up in the passive phase. This allows us to remove a `schedulePassiveEffectCallback` call from the synchronous unmount path. Co-authored-by: Brian Vaughn <bvaughn@fb.com>

view details

push time in 6 days

PR merged facebook/react

Check for passive effects on the root fiber CLA Signed React Core Team

The root fiber doesn't have a parent from which we can read the subtreeTag, so we need to check its effectTag directly.

The root fiber previously did not have any pending passive effects, but it does now that deleted fibers are cleaned up in the passive phase.

This allows us to remove a schedulePassiveEffectCallback call from the synchronous unmount path.

+4 -3

3 comments

2 changed files

acdlite

pr closed time in 6 days

push eventacdlite/react

Andrew Clark

commit sha e1f96b82b4aff1d5c50d02c76df9c60e32dba408

Check PassiveStatic instead of Passive (#19489) Saves us from having to set a flag on `current` during the layout phase. Could result in some redundant traversal, since PassiveStatic includes effects that don't need clean-up. But it's worth it to remove the work from the layout phase. While I was editing this, I also re-arranged it so that we check the `effectTag` check before we check the `tag`, since the `effectTag` check is the one that's more likely to fail.

view details

Brian Vaughn

commit sha 14f774ae89f3b40140ab7a12965f5705b0cb7d9d

Merge branch 'master' into root-passive-effect-check

view details

push time in 6 days

push eventfacebook/react

Andrew Clark

commit sha e1f96b82b4aff1d5c50d02c76df9c60e32dba408

Check PassiveStatic instead of Passive (#19489) Saves us from having to set a flag on `current` during the layout phase. Could result in some redundant traversal, since PassiveStatic includes effects that don't need clean-up. But it's worth it to remove the work from the layout phase. While I was editing this, I also re-arranged it so that we check the `effectTag` check before we check the `tag`, since the `effectTag` check is the one that's more likely to fail.

view details

push time in 6 days

PR merged facebook/react

Check PassiveStatic instead of Passive CLA Signed React Core Team

Saves us from having to set a flag on current during the layout phase.

Could result in some redundant traversal, since PassiveStatic includes effects that don't need clean-up. But it's worth it to remove the work from the layout phase.

While I was editing this, I also re-arranged it so that we check the effectTag check before we check the tag, since the effectTag check is the one that's more likely to fail.

+6 -10

3 comments

2 changed files

acdlite

pr closed time in 6 days

push eventbvaughn/react

Ricky

commit sha 7c8cc4358e79670d1e3be803a8c5267116e09bff

Add postTask browser scheduler implementation (#19479) * Reduce code to necessities * Switch to postTask API * Add SchedulerPostTask tests * Updates from review * Fix typo from review * Generate build of unstable_post_task

view details

Dan Abramov

commit sha 291db05a756dd88d0f687b3083e85a22abbf5214

Add regression tests for all events (#19485)

view details

Ricky

commit sha 74cd7e5f17e801f89c88689ecd9560a342b95c2c

Use feature flags for React Native in the test renderer (#19486)

view details

Brian Vaughn

commit sha eae90cdbe90ab6ad8c48cee6f5af10e767a3f9fc

Effects list refactor continued: passive effects traversal (#19374) * Adds new `Passive` subtree tag value. * Adds recursive traversal for passive effects (mounts and unmounts). * Removes `pendingPassiveHookEffectsMount` and `pendingPassiveHookEffectsUnmount` arrays from work loop. * Re-adds sibling and child pointer detaching (temporarily removed in previous PR). * Addresses some minor TODO comments left over from previous PRs. --- Co-authored-by: Luna Ruan <luna@fb.com>

view details

Dan Abramov

commit sha dff97a6915ef3a3897f85e3e47ffa55d4714ae72

Fix onGot/LostPointerCapture events (#19487)

view details

Brian Vaughn

commit sha 349015244e7ea51a940721afbaf14a1db3ce8cad

Moved resetChildLanes into complete work This enabled us to remove a few hot path tag-type checks, but did not otherwise change any functionality.

view details

push time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;++                current.effectTag |= Passive;++                if (__DEV__) {+                  // This flag is used to avoid warning about an update to an unmounted component+                  // if the component has a passive unmount scheduled.+                  // Presumably the listener would be cleaned up by that unmount.+                  current.effectTag |= PassiveUnmountPendingDev;+                  const alternate = current.alternate;+                  if (alternate !== null) {+                    alternate.effectTag |= PassiveUnmountPendingDev;+                  }+                }++                schedulePassiveEffectCallback();

Sorry, please disregard my other comment. I thought I was responding to a different thread when I got the email notification. :smile:

bvaughn

comment created time in 6 days

push eventfacebook/react

Brian Vaughn

commit sha eae90cdbe90ab6ad8c48cee6f5af10e767a3f9fc

Effects list refactor continued: passive effects traversal (#19374) * Adds new `Passive` subtree tag value. * Adds recursive traversal for passive effects (mounts and unmounts). * Removes `pendingPassiveHookEffectsMount` and `pendingPassiveHookEffectsUnmount` arrays from work loop. * Re-adds sibling and child pointer detaching (temporarily removed in previous PR). * Addresses some minor TODO comments left over from previous PRs. --- Co-authored-by: Luna Ruan <luna@fb.com>

view details

push time in 6 days

PR merged facebook/react

Reviewers
Effects list refactor continued: passive effects traversal CLA Signed React Core Team
  • Adds new Passive subtree tag value.
  • Bubbles passive flag to ancestors in the case of an unmount.
  • Adds recursive traversal for passive effects (mounts and unmounts).
  • Removes pendingPassiveHookEffectsMount and pendingPassiveHookEffectsUnmount arrays from work loop.
  • Re-adds sibling and child pointer detaching (temporarily removed in previous PR).
  • Addresses some minor TODO comments left over from previous PRs.

Co-authored-by: Luna Ruan luna@fb.com

+458 -274

11 comments

11 changed files

bvaughn

pr closed time in 6 days

pull request commentfacebook/react

Effects list refactor continued: passive effects traversal

No problem. Thanks for the fast feedback!

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;++                current.effectTag |= Passive;++                if (__DEV__) {+                  // This flag is used to avoid warning about an update to an unmounted component+                  // if the component has a passive unmount scheduled.+                  // Presumably the listener would be cleaned up by that unmount.+                  current.effectTag |= PassiveUnmountPendingDev;+                  const alternate = current.alternate;+                  if (alternate !== null) {+                    alternate.effectTag |= PassiveUnmountPendingDev;+                  }+                }++                schedulePassiveEffectCallback();

The return pointer has been cleared already though

bvaughn

comment created time in 6 days

pull request commentfacebook/react

Effects list refactor continued: passive effects traversal

There's been a lot of back and forth. I think I've made all of the suggested changes (that I can make). Let me know if I missed something!

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {   } } -export function enqueuePendingPassiveHookEffectMount(-  fiber: Fiber,-  effect: HookEffect,-): void {-  pendingPassiveHookEffectsMount.push(effect, fiber);-  if (!rootDoesHavePassiveEffects) {-    rootDoesHavePassiveEffects = true;-    scheduleCallback(NormalSchedulerPriority, () => {-      flushPassiveEffects();-      return null;-    });-  }+function invokePassiveEffectCreate(effect: HookEffect): void {+  const create = effect.create;+  effect.destroy = create(); } -export function enqueuePendingPassiveHookEffectUnmount(-  fiber: Fiber,-  effect: HookEffect,-): void {-  pendingPassiveHookEffectsUnmount.push(effect, fiber);-  if (__DEV__) {-    fiber.effectTag |= PassiveUnmountPendingDev;-    const alternate = fiber.alternate;-    if (alternate !== null) {-      alternate.effectTag |= PassiveUnmountPendingDev;+function flushPassiveMountEffects(firstChild: Fiber): void {+  let fiber = firstChild;+  while (fiber !== null) {+    const didBailout =+      fiber.alternate !== null && fiber.alternate.child === fiber.child;+    const primarySubtreeTag = fiber.subtreeTag & PassiveSubtreeTag;++    if (+      fiber.child !== null &&+      !didBailout &&+      primarySubtreeTag !== NoSubtreeTag+    ) {+      flushPassiveMountEffects(fiber.child);+    }++    if ((fiber.effectTag & Update) !== NoEffect) {+      switch (fiber.tag) {+        case FunctionComponent:+        case ForwardRef:+        case SimpleMemoComponent:+        case Block: {+          flushPassiveMountEffectsImpl(fiber);+        }+      }     }++    fiber = fiber.sibling;   }-  if (!rootDoesHavePassiveEffects) {-    rootDoesHavePassiveEffects = true;-    scheduleCallback(NormalSchedulerPriority, () => {-      flushPassiveEffects();-      return null;-    });+}++function flushPassiveMountEffectsImpl(fiber: Fiber): void {+  const updateQueue: FunctionComponentUpdateQueue | null = (fiber.updateQueue: any);+  const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;+  if (lastEffect !== null) {+    const firstEffect = lastEffect.next;+    let effect = firstEffect;+    do {+      const {next, tag} = effect;++      if (+        (tag & HookPassive) !== NoHookEffect &&+        (tag & HookHasEffect) !== NoHookEffect+      ) {+        if (__DEV__) {+          setCurrentDebugFiberInDEV(fiber);+          if (+            enableProfilerTimer &&+            enableProfilerCommitHooks &&+            fiber.mode & ProfileMode+          ) {+            startPassiveEffectTimer();+            invokeGuardedCallback(+              null,+              invokePassiveEffectCreate,+              null,+              effect,+            );+            recordPassiveEffectDuration(fiber);+          } else {+            invokeGuardedCallback(+              null,+              invokePassiveEffectCreate,+              null,+              effect,+            );+          }+          if (hasCaughtError()) {+            invariant(fiber !== null, 'Should be working on an effect.');+            const error = clearCaughtError();+            captureCommitPhaseError(fiber, error);+          }+          resetCurrentDebugFiberInDEV();+        } else {+          try {+            const create = effect.create;+            if (+              enableProfilerTimer &&+              enableProfilerCommitHooks &&+              fiber.mode & ProfileMode+            ) {+              try {+                startPassiveEffectTimer();+                effect.destroy = create();+              } finally {+                recordPassiveEffectDuration(fiber);+              }+            } else {+              effect.destroy = create();+            }+          } catch (error) {+            invariant(fiber !== null, 'Should be working on an effect.');+            captureCommitPhaseError(fiber, error);+          }+        }+      }++      effect = next;+    } while (effect !== firstEffect);   } } -function invokePassiveEffectCreate(effect: HookEffect): void {-  const create = effect.create;-  effect.destroy = create();+function flushPassiveUnmountEffects(firstChild: Fiber): void {+  let fiber = firstChild;+  while (fiber !== null) {+    const deletions = fiber.deletions;+    if (deletions !== null) {+      for (let i = 0; i < deletions.length; i++) {+        const fiberToDelete = deletions[i];+        // If this fiber (or anything below it) has passive effects then traverse the subtree.+        const primaryEffectTag = fiberToDelete.effectTag & PassiveMask;+        const primarySubtreeTag = fiberToDelete.subtreeTag & PassiveSubtreeTag;+        if (+          primarySubtreeTag !== NoSubtreeTag ||+          primaryEffectTag !== NoEffect+        ) {+          flushPassiveUnmountEffects(fiberToDelete);

@acdlite Let me know if ae37c7c addresses your suggestion here?

bvaughn

comment created time in 6 days

push eventbvaughn/react

Brian Vaughn

commit sha ae37c7c2620f8b202426dc4f94c2d8797a54544d

Split flushPassiveUnmountEffects() into two functions One is used for mounted fibers, and one for unmounted subtrees.

view details

push time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {   } } -export function enqueuePendingPassiveHookEffectMount(-  fiber: Fiber,-  effect: HookEffect,-): void {-  pendingPassiveHookEffectsMount.push(effect, fiber);-  if (!rootDoesHavePassiveEffects) {-    rootDoesHavePassiveEffects = true;-    scheduleCallback(NormalSchedulerPriority, () => {-      flushPassiveEffects();-      return null;-    });-  }+function invokePassiveEffectCreate(effect: HookEffect): void {+  const create = effect.create;+  effect.destroy = create(); } -export function enqueuePendingPassiveHookEffectUnmount(-  fiber: Fiber,-  effect: HookEffect,-): void {-  pendingPassiveHookEffectsUnmount.push(effect, fiber);-  if (__DEV__) {-    fiber.effectTag |= PassiveUnmountPendingDev;-    const alternate = fiber.alternate;-    if (alternate !== null) {-      alternate.effectTag |= PassiveUnmountPendingDev;+function flushPassiveMountEffects(firstChild: Fiber): void {+  let fiber = firstChild;+  while (fiber !== null) {+    const didBailout =+      fiber.alternate !== null && fiber.alternate.child === fiber.child;+    const primarySubtreeTag = fiber.subtreeTag & PassiveSubtreeTag;++    if (+      fiber.child !== null &&+      !didBailout &&+      primarySubtreeTag !== NoSubtreeTag+    ) {+      flushPassiveMountEffects(fiber.child);+    }++    if ((fiber.effectTag & Update) !== NoEffect) {+      switch (fiber.tag) {+        case FunctionComponent:+        case ForwardRef:+        case SimpleMemoComponent:+        case Block: {+          flushPassiveMountEffectsImpl(fiber);+        }+      }     }++    fiber = fiber.sibling;   }-  if (!rootDoesHavePassiveEffects) {-    rootDoesHavePassiveEffects = true;-    scheduleCallback(NormalSchedulerPriority, () => {-      flushPassiveEffects();-      return null;-    });+}++function flushPassiveMountEffectsImpl(fiber: Fiber): void {+  const updateQueue: FunctionComponentUpdateQueue | null = (fiber.updateQueue: any);+  const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;+  if (lastEffect !== null) {+    const firstEffect = lastEffect.next;+    let effect = firstEffect;+    do {+      const {next, tag} = effect;++      if (+        (tag & HookPassive) !== NoHookEffect &&+        (tag & HookHasEffect) !== NoHookEffect+      ) {+        if (__DEV__) {+          setCurrentDebugFiberInDEV(fiber);+          if (+            enableProfilerTimer &&+            enableProfilerCommitHooks &&+            fiber.mode & ProfileMode+          ) {+            startPassiveEffectTimer();+            invokeGuardedCallback(+              null,+              invokePassiveEffectCreate,+              null,+              effect,+            );+            recordPassiveEffectDuration(fiber);+          } else {+            invokeGuardedCallback(+              null,+              invokePassiveEffectCreate,+              null,+              effect,+            );+          }+          if (hasCaughtError()) {+            invariant(fiber !== null, 'Should be working on an effect.');+            const error = clearCaughtError();+            captureCommitPhaseError(fiber, error);+          }+          resetCurrentDebugFiberInDEV();+        } else {+          try {+            const create = effect.create;+            if (+              enableProfilerTimer &&+              enableProfilerCommitHooks &&+              fiber.mode & ProfileMode+            ) {+              try {+                startPassiveEffectTimer();+                effect.destroy = create();+              } finally {+                recordPassiveEffectDuration(fiber);+              }+            } else {+              effect.destroy = create();+            }+          } catch (error) {+            invariant(fiber !== null, 'Should be working on an effect.');+            captureCommitPhaseError(fiber, error);+          }+        }+      }++      effect = next;+    } while (effect !== firstEffect);   } } -function invokePassiveEffectCreate(effect: HookEffect): void {-  const create = effect.create;-  effect.destroy = create();+function flushPassiveUnmountEffects(firstChild: Fiber): void {+  let fiber = firstChild;+  while (fiber !== null) {+    const deletions = fiber.deletions;+    if (deletions !== null) {+      for (let i = 0; i < deletions.length; i++) {+        const fiberToDelete = deletions[i];+        // If this fiber (or anything below it) has passive effects then traverse the subtree.+        const primaryEffectTag = fiberToDelete.effectTag & PassiveMask;+        const primarySubtreeTag = fiberToDelete.subtreeTag & PassiveSubtreeTag;+        if (+          primarySubtreeTag !== NoSubtreeTag ||+          primaryEffectTag !== NoEffect+        ) {+          flushPassiveUnmountEffects(fiberToDelete);

So for example, it doesn't have to check for nested deletions, because you're already inside a deleted tree.

This is a reasonable point. I can split the two functions apart, if you think the added bytes is better than the unnecessary null check.

In other words, we shouldn't have to null out finishedWork.deletions once we're done processing them

Clearing the deletions array was unnecessary. I'll remove that line.

bvaughn

comment created time in 6 days

issue closedfacebook/react

Each Class is executing two times in react version 16.13.1

<!-- I just created a new project using command "npx create-react-app" hello-world. It provided me a working directory 'hello-world'. In version 16.13.1, we get by default functional component in 'app.js'. So I changed it to class component. Now I just added a console log and ran the command "npm start", After clicking on Inspect in the browser, in Console I found that the line I console logged was printed two times. I refreshed and found same result again. It should be printed only once. -->

React version: 16.13.1 React Script Version: 3.4.1

Steps To Reproduce

  1. Just create a new project using command npx create-react-app <project name>
  2. Convert functional component to Class component in App.js
  3. Add a console log before "return" statement.
  4. then run command "npm start"
  5. In the console in browser, you will see console log was printed two times.

<!-- Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Issues without reproduction steps or code examples may be immediately closed as not actionable. -->

Link to code example: https://github.com/radhey7705/React-Project.git

The current behavior

after run npm start, Class component is working two times

The expected behavior

after run npm start, Class component should only work single time.

closed time in 6 days

radhey7705

issue commentfacebook/react

Each Class is executing two times in react version 16.13.1

This is intentional, as mentioned above :)

radhey7705

comment created time in 6 days

push eventbvaughn/react

Brian Vaughn

commit sha c1a1be521766a8ba746e7268ec044516c4a9dc50

Removed unnecessary bailout check. Added a TODO for followup.

view details

push time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;++                current.effectTag |= Passive;++                if (__DEV__) {+                  // This flag is used to avoid warning about an update to an unmounted component+                  // if the component has a passive unmount scheduled.+                  // Presumably the listener would be cleaned up by that unmount.+                  current.effectTag |= PassiveUnmountPendingDev;+                  const alternate = current.alternate;+                  if (alternate !== null) {+                    alternate.effectTag |= PassiveUnmountPendingDev;+                  }+                }++                schedulePassiveEffectCallback();

Yeah. My bad, I should have left a more thorough response initially.

A Deletion effect would get bubbled into a PassiveSubtree tag, but that bubbling happens during the render phase, whereas this function is in the commit phase. (There's no bubbling after this point.)

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;++                current.effectTag |= Passive;

I'm going to leave this ^ to the TODO as well.

bvaughn

comment created time in 6 days

push eventbvaughn/react

Brian Vaughn

commit sha a57840eca99b8fa8675d789e248d042efa9791fc

Defer clearing deletions array until passive, remove unecessary didBailout check

view details

Brian Vaughn

commit sha 6d926dadde31acbedc2914cd478d0743f14fff65

Removed PassiveUnmountPendingDev in the new reconciler

view details

push time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;++                current.effectTag |= Passive;++                if (__DEV__) {+                  // This flag is used to avoid warning about an update to an unmounted component+                  // if the component has a passive unmount scheduled.+                  // Presumably the listener would be cleaned up by that unmount.+                  current.effectTag |= PassiveUnmountPendingDev;

I don't know. I'll make this change but I don't know that it's worth doing. :)

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;++                current.effectTag |= Passive;++                if (__DEV__) {+                  // This flag is used to avoid warning about an update to an unmounted component+                  // if the component has a passive unmount scheduled.+                  // Presumably the listener would be cleaned up by that unmount.+                  current.effectTag |= PassiveUnmountPendingDev;

So essentially this:

// If there are pending passive effects unmounts for this Fiber,
// we can assume that they would have prevented this update.
if ((fiber.effectTag & PassiveUnmountPendingDev) !== NoEffect) {
  return;
}

Would become this:

const alternate = fiber.alternate;
if ((fiber.effectTag & PassiveStatic) !== NoEffect) {
  const updateQueue: FunctionComponentUpdateQueue | null = (fiber.updateQueue: any);
  if (updateQueue !== null) {
    const lastEffect = updateQueue.lastEffect;
    if (lastEffect !== null) {
      const firstEffect = lastEffect.next;

      let effect = firstEffect;
      do {
        if (effect.destroy !== undefined) {
          if ((effect.tag & HookPassive) !== NoHookEffect) {
            return;
          }
        }
        effect = effect.next;
      } while (effect !== firstEffect);
    }
  }
}

The latter is clearly worse, but it's lazy and it's DEV only code, so it seems like an improvement.

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;++                current.effectTag |= Passive;++                if (__DEV__) {+                  // This flag is used to avoid warning about an update to an unmounted component+                  // if the component has a passive unmount scheduled.+                  // Presumably the listener would be cleaned up by that unmount.+                  current.effectTag |= PassiveUnmountPendingDev;

Hm, maybe...

If we weren't careful, we might miss legit warnings that way. We wouldn't want to incorrectly suppress a warning for a state update for a component that's within a tree that happened to have a subtree tag, even if the component in question doesn't have a passive effect scheduled. I guess we could walk the return path to see if an ancestor has PassiveUnmountPendingDev, then walk the Fiber receiving the update to see if its effect list contains a passive effect.

But as I write that, I realize that if we are willing to walk the effect list, we don't really need to look at an ancestor. We could just check for a Passive effect on the fiber an update is being scheduled for, then walk its effects list to confirm there's actually a destroy function. So I think we could maybe remove the PassiveUnmountPendingDev bit entirely?

Let me take a pass at that.

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;++                current.effectTag |= Passive;++                if (__DEV__) {+                  // This flag is used to avoid warning about an update to an unmounted component+                  // if the component has a passive unmount scheduled.+                  // Presumably the listener would be cleaned up by that unmount.+                  current.effectTag |= PassiveUnmountPendingDev;+                  const alternate = current.alternate;+                  if (alternate !== null) {+                    alternate.effectTag |= PassiveUnmountPendingDev;+                  }+                }++                schedulePassiveEffectCallback();

It's still necessary.

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {   } } -export function enqueuePendingPassiveHookEffectMount(-  fiber: Fiber,-  effect: HookEffect,-): void {-  pendingPassiveHookEffectsMount.push(effect, fiber);-  if (!rootDoesHavePassiveEffects) {-    rootDoesHavePassiveEffects = true;-    scheduleCallback(NormalSchedulerPriority, () => {-      flushPassiveEffects();-      return null;-    });-  }+function invokePassiveEffectCreate(effect: HookEffect): void {+  const create = effect.create;+  effect.destroy = create(); } -export function enqueuePendingPassiveHookEffectUnmount(-  fiber: Fiber,-  effect: HookEffect,-): void {-  pendingPassiveHookEffectsUnmount.push(effect, fiber);-  if (__DEV__) {-    fiber.effectTag |= PassiveUnmountPendingDev;-    const alternate = fiber.alternate;-    if (alternate !== null) {-      alternate.effectTag |= PassiveUnmountPendingDev;+function flushPassiveMountEffects(firstChild: Fiber): void {+  let fiber = firstChild;+  while (fiber !== null) {+    const didBailout =+      fiber.alternate !== null && fiber.alternate.child === fiber.child;+    const primarySubtreeTag = fiber.subtreeTag & PassiveSubtreeTag;++    if (+      fiber.child !== null &&+      !didBailout &&+      primarySubtreeTag !== NoSubtreeTag+    ) {+      flushPassiveMountEffects(fiber.child);+    }++    if ((fiber.effectTag & Update) !== NoEffect) {+      switch (fiber.tag) {+        case FunctionComponent:+        case ForwardRef:+        case SimpleMemoComponent:+        case Block: {+          flushPassiveMountEffectsImpl(fiber);+        }+      }     }++    fiber = fiber.sibling;   }-  if (!rootDoesHavePassiveEffects) {-    rootDoesHavePassiveEffects = true;-    scheduleCallback(NormalSchedulerPriority, () => {-      flushPassiveEffects();-      return null;-    });+}++function flushPassiveMountEffectsImpl(fiber: Fiber): void {+  const updateQueue: FunctionComponentUpdateQueue | null = (fiber.updateQueue: any);+  const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;+  if (lastEffect !== null) {+    const firstEffect = lastEffect.next;+    let effect = firstEffect;+    do {+      const {next, tag} = effect;++      if (+        (tag & HookPassive) !== NoHookEffect &&+        (tag & HookHasEffect) !== NoHookEffect+      ) {+        if (__DEV__) {+          setCurrentDebugFiberInDEV(fiber);+          if (+            enableProfilerTimer &&+            enableProfilerCommitHooks &&+            fiber.mode & ProfileMode+          ) {+            startPassiveEffectTimer();+            invokeGuardedCallback(+              null,+              invokePassiveEffectCreate,+              null,+              effect,+            );+            recordPassiveEffectDuration(fiber);+          } else {+            invokeGuardedCallback(+              null,+              invokePassiveEffectCreate,+              null,+              effect,+            );+          }+          if (hasCaughtError()) {+            invariant(fiber !== null, 'Should be working on an effect.');+            const error = clearCaughtError();+            captureCommitPhaseError(fiber, error);+          }+          resetCurrentDebugFiberInDEV();+        } else {+          try {+            const create = effect.create;+            if (+              enableProfilerTimer &&+              enableProfilerCommitHooks &&+              fiber.mode & ProfileMode+            ) {+              try {+                startPassiveEffectTimer();+                effect.destroy = create();+              } finally {+                recordPassiveEffectDuration(fiber);+              }+            } else {+              effect.destroy = create();+            }+          } catch (error) {+            invariant(fiber !== null, 'Should be working on an effect.');+            captureCommitPhaseError(fiber, error);+          }+        }+      }++      effect = next;+    } while (effect !== firstEffect);   } } -function invokePassiveEffectCreate(effect: HookEffect): void {-  const create = effect.create;-  effect.destroy = create();+function flushPassiveUnmountEffects(firstChild: Fiber): void {+  let fiber = firstChild;+  while (fiber !== null) {+    const deletions = fiber.deletions;+    if (deletions !== null) {+      for (let i = 0; i < deletions.length; i++) {+        const fiberToDelete = deletions[i];+        // If this fiber (or anything below it) has passive effects then traverse the subtree.+        const primaryEffectTag = fiberToDelete.effectTag & PassiveMask;+        const primarySubtreeTag = fiberToDelete.subtreeTag & PassiveSubtreeTag;+        if (+          primarySubtreeTag !== NoSubtreeTag ||+          primaryEffectTag !== NoEffect+        ) {+          flushPassiveUnmountEffects(fiberToDelete);

They don't really do different things. They traverse a tree and execute any passive effects that are scheduled.

This might be related to the didBailout thing?

Which thing?

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;

Sure. I can add such a comment so we can follow up and consider the change later 👍

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;++                current.effectTag |= Passive;

Hm, maybe. We'd need to track when we're inside of an unmounted tree, so we would know to always run passive effects (regardless of HookHasEffect).

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;

What do you mean by, "the rest of this block"?

We still need PassiveUnmountPendingDev in DEV in order to avoid over-warning for state updates triggered by passive effect subscriptions.

I'm not sure what you want the TODO to say.

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitRootImpl(root, renderPriorityLevel) {       markLayoutEffectsStopped();     } +    // If there are pending passive effects, schedule a callback to process them.+    if ((root.current.subtreeTag & PassiveSubtreeTag) !== NoSubtreeTag) {

Oh, duh.

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitMutationEffects( ) {   let fiber = firstChild;   while (fiber !== null) {-    if (fiber.deletions !== null) {-      commitMutationEffectsDeletions(-        fiber.deletions,-        root,-        renderPriorityLevel,-      );+    const deletions = fiber.deletions;+    if (deletions !== null) {+      commitMutationEffectsDeletions(deletions, root, renderPriorityLevel); -      // TODO (effects) Don't clear this yet; we may need to cleanup passive effects-      fiber.deletions = null;+      // If there are no pending passive effects, clear the deletions Array.

Hm, true. Same concern as previously expressed about the number of subtle changes (many non-observable) hinge on the fact that we removed ab optimization though.

bvaughn

comment created time in 6 days

push eventbvaughn/react

Brian Vaughn

commit sha 9dc5188110e14bcf3f73c0dcfa7ebca0d6849f95

Tweaks made in response to PR feedback: * Added Deletion effect to PassiveMask. * Removed the no longer needed explicit bubbling of Passive subtreeTag during deletion. * Removed unnecessary deletion effectTag from fallback child. * Detached alternate.return as well during layout. * Removed PassiveStaticEffect from hook update. * Removed unnecessary call to detachFiberAfterEffects.

view details

Brian Vaughn

commit sha ac157d0d930dcc26846838d5ca41afa408be5bbd

Moved resetChildLanes into complete work This enabled us to remove a few hot path tag-type checks, but did not otherwise change any functionality.

view details

push time in 6 days

push eventbvaughn/react

Brian Vaughn

commit sha 9dc5188110e14bcf3f73c0dcfa7ebca0d6849f95

Tweaks made in response to PR feedback: * Added Deletion effect to PassiveMask. * Removed the no longer needed explicit bubbling of Passive subtreeTag during deletion. * Removed unnecessary deletion effectTag from fallback child. * Detached alternate.return as well during layout. * Removed PassiveStaticEffect from hook update. * Removed unnecessary call to detachFiberAfterEffects.

view details

push time in 6 days

pull request commentfacebook/react

Effects list refactor continued: passive effects traversal

Thanks for the feedback @acdlite! 😄

Made the suggested tweaks:

  • Added Deletion effect to PassiveMask.
    • Removed the no longer needed explicit bubbling of Passive subtreeTag during deletion.
  • Removed unnecessary deletion effectTag from fallback child.
  • Detached alternate.return as well during layout.
  • Removed PassiveStaticEffect from hook update.
  • Removed unnecessary call to detachFiberAfterEffects.
bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitMutationEffectsDeletions(         captureCommitPhaseError(childToDelete, error);       }     }-    // Don't clear the Deletion effect yet; we also use it to know when we need to detach refs later.++    // If there are no pending passive effects, it's safe to detach remaining pointers now.+    const primarySubtreeTag = childToDelete.subtreeTag & PassiveSubtreeTag;+    const primaryEffectTag = childToDelete.effectTag & PassiveMask;+    if (primarySubtreeTag === NoSubtreeTag && primaryEffectTag === NoEffect) {+      detachFiberAfterEffects(childToDelete);

Ah, yeah fair enough. It is safe to remove now.

I'm a tiny bit concerned that it's not intuitive how re-adding that optimization would impact a few of these places, but maybe that won't actually be a concern.

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitMutationEffectsDeletions(         captureCommitPhaseError(childToDelete, error);       }     }-    // Don't clear the Deletion effect yet; we also use it to know when we need to detach refs later.++    // If there are no pending passive effects, it's safe to detach remaining pointers now.+    const primarySubtreeTag = childToDelete.subtreeTag & PassiveSubtreeTag;+    const primaryEffectTag = childToDelete.effectTag & PassiveMask;+    if (primarySubtreeTag === NoSubtreeTag && primaryEffectTag === NoEffect) {+      detachFiberAfterEffects(childToDelete);

Yes. I know. Adding Deletion to the static mask means we can remove 4 of these places. ;)

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function updateEffect(     }   }   return updateEffectImpl(-    UpdateEffect | PassiveEffect,+    UpdateEffect | PassiveEffect | PassiveStaticEffect,

Yeah I guess that's fair.

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function mountEffect(     }   }   return mountEffectImpl(-    UpdateEffect | PassiveEffect,+    UpdateEffect | PassiveEffect | PassiveStaticEffect,

mountEffectImpl is used for layout effects also. This static flag only applies to passive effects.

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function ChildReconciler(shouldTrackSideEffects) {       returnFiber.deletions = [childToDelete];       // TODO (effects) Rename this to better reflect its new usage (e.g. ChildDeletions)       returnFiber.effectTag |= Deletion;++      // We are deleting a subtree that may contain a passive effect.+      // Mark the parent so we traverse this path after commit and run any unmount functions.+      // This may cause us to traverse unnecessarily in some cases, but effects are common,+      // and the cost of over traversing is small (just the path to the deleted node).+      returnFiber.subtreeTag |= PassiveSubtreeTag;

Hm, yes, I think that would work. Sorry if I misinterpreted your original suggestion. I realize now that you were probably suggesting that instead of this.

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function updateSuspensePrimaryChildren(   if (currentFallbackChildFragment !== null) {     // Delete the fallback child fragment     currentFallbackChildFragment.nextEffect = null;-    currentFallbackChildFragment.effectTag = Deletion;+    currentFallbackChildFragment.effectTag =+      (currentFallbackChildFragment.effectTag & StaticMask) | Deletion;     workInProgress.firstEffect = workInProgress.lastEffect = currentFallbackChildFragment;     const deletions = workInProgress.deletions;     if (deletions === null) {       workInProgress.deletions = [currentFallbackChildFragment];       // TODO (effects) Rename this to better reflect its new usage (e.g. ChildDeletions)       workInProgress.effectTag |= Deletion;++      // We are deleting a subtree that may contain a passive effect.+      // Mark the parent so we traverse this path after commit and run any unmount functions.+      // This may cause us to traverse unnecessarily in some cases, but effects are common,+      // and the cost of over traversing is small (just the path to the deleted node).+      workInProgress.subtreeTag |= PassiveSubtreeTag;

Right. I assumed that comment applied to all 4 places we were bubbling :)

bvaughn

comment created time in 6 days

push eventbvaughn/react

Dan Abramov

commit sha 0eea16601cab75dbb698d67220bce5674a13e38a

Event propagation test suite (#19483)

view details

Brian Vaughn

commit sha bfe385ff4c8ff4d22bd6263c79b2316e17604944

Effects list refactor continued: passive effects traversal * Adds new Passive subtree tag value. * Bubbles passive flag to ancestors in the case of an unmount. * Adds recursive traversal for passive effects (mounts and unmounts). * Removes pendingPassiveHookEffectsMount and pendingPassiveHookEffectsUnmount arrays from work loop. * Re-adds sibling and child pointer detaching (temporarily removed in previous PR). * Addresses some minor TODO comments left over from previous PRs.

view details

Brian Vaughn

commit sha fe2e311db5db1ae3ed2b1263ac577438e45bb204

Add persistent, Static subtreeTag This allows certain concepts to persist without recalculting them, e.g. whether a subtree contains passive effects or portals. This helps for cases like nested unmounts that contain deep passive effects.

view details

Brian Vaughn

commit sha 2876d61f583e3619db0cfd99a4628049b7bd88cf

Add static effectTag bit for passive effect cleanup after unmount

view details

Brian Vaughn

commit sha 0c59d069aae3c7d0654344cb10ae83c5c9cc3b24

Added gate to SchedulingProfiler test

view details

Brian Vaughn

commit sha 7d618ede1794700c287552a1f5e816e6f2586a43

Defer Fiber field clenaup to passive phase for all but return pointer

view details

Brian Vaughn

commit sha cba61f7f443d7a93d4072c6bd23147a2d9ee2ed4

Remove PassiveSubtreeTag optimization Instead, always schedule a passive traversal for a subtree containing a deleted node. The overhead of doing this (during the passive phase) is small, and effects are so common that a cleanup in the subtree is likely. This saves some bytes.

view details

Brian Vaughn

commit sha 9faa0912454b07ed00c202af4afbdeffe0c77d44

Moved resetChildLanes into complete work This enabled us to remove a few hot path tag-type checks, but did not otherwise change any functionality.

view details

push time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function ChildReconciler(shouldTrackSideEffects) {       returnFiber.deletions = [childToDelete];       // TODO (effects) Rename this to better reflect its new usage (e.g. ChildDeletions)       returnFiber.effectTag |= Deletion;++      // If we are deleting a subtree that contains a passive effect,+      // mark the parent so that we're sure to traverse after commit and run any unmount functions.+      const primaryEffectTag = childToDelete.effectTag & PassiveMask;+      const primarySubtreeTag = childToDelete.subtreeTag & PassiveSubtreeTag;+      if (primaryEffectTag !== NoEffect || primarySubtreeTag !== NoSubtreeTag) {

That's a good point I guess. I hadn't considered that aspect.

I consider the "detach" methods as kind of an optional optimization.

bvaughn

comment created time in 6 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function ChildReconciler(shouldTrackSideEffects) {       returnFiber.deletions = [childToDelete];       // TODO (effects) Rename this to better reflect its new usage (e.g. ChildDeletions)       returnFiber.effectTag |= Deletion;++      // If we are deleting a subtree that contains a passive effect,+      // mark the parent so that we're sure to traverse after commit and run any unmount functions.+      const primaryEffectTag = childToDelete.effectTag & PassiveMask;+      const primarySubtreeTag = childToDelete.subtreeTag & PassiveSubtreeTag;+      if (primaryEffectTag !== NoEffect || primarySubtreeTag !== NoSubtreeTag) {

That's a good point.

bvaughn

comment created time in 6 days

push eventbvaughn/react

Brian Vaughn

commit sha cba61f7f443d7a93d4072c6bd23147a2d9ee2ed4

Remove PassiveSubtreeTag optimization Instead, always schedule a passive traversal for a subtree containing a deleted node. The overhead of doing this (during the passive phase) is small, and effects are so common that a cleanup in the subtree is likely. This saves some bytes.

view details

push time in 7 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function ChildReconciler(shouldTrackSideEffects) {       returnFiber.deletions = [childToDelete];       // TODO (effects) Rename this to better reflect its new usage (e.g. ChildDeletions)       returnFiber.effectTag |= Deletion;++      // If we are deleting a subtree that contains a passive effect,+      // mark the parent so that we're sure to traverse after commit and run any unmount functions.+      const primaryEffectTag = childToDelete.effectTag & PassiveMask;+      const primarySubtreeTag = childToDelete.subtreeTag & PassiveSubtreeTag;+      if (primaryEffectTag !== NoEffect || primarySubtreeTag !== NoSubtreeTag) {

Maybe this optimization isn't worth it and we should always set the PassiveSubtreeTag on the parent when a child is deleted.

Given that the concept of static flags seems like a positive– and so keeping PassiveStatic around doesn't add any net overhead– the only question seems to be whether the few extra bytes required for us to check effectTag and subtreeTag are worth it, yeah?

With the current check, we might over traverse in the event that a child was deleted that had a passive effect, but no cleanup function. (Seems unlikely.) If we removed this check, we might over traverse any time a leaf node is deleted, period. Still, I guess that wouldn't be that bad since we'd only traverse the path to where the leaf was deleted.

TBH I think the cost is pretty small on either side (number or bytes or potential over traversing). Happy to remove the check for now.

bvaughn

comment created time in 7 days

pull request commentMLH-Fellowship/scheduling-profiler-prototype

Add Chromium flamechart colors

Ah! I didn't realize that. This script URL-based coloring is something that I find really useful in Chrome's profiler, and sadly it's missing from Firefox's. Let's see what users think of this.

Cool! I dig it.

Oops, should've mentioned the screenshots. There's also a Vercel deploy preview, which also includes flamechart hovering optimizations - we have 60FPS flamechart hovers now 🎉

Exciting! Great work :smile:

taneliang

comment created time in 7 days

push eventbvaughn/react

Dan Abramov

commit sha 0eea16601cab75dbb698d67220bce5674a13e38a

Event propagation test suite (#19483)

view details

Brian Vaughn

commit sha bfe385ff4c8ff4d22bd6263c79b2316e17604944

Effects list refactor continued: passive effects traversal * Adds new Passive subtree tag value. * Bubbles passive flag to ancestors in the case of an unmount. * Adds recursive traversal for passive effects (mounts and unmounts). * Removes pendingPassiveHookEffectsMount and pendingPassiveHookEffectsUnmount arrays from work loop. * Re-adds sibling and child pointer detaching (temporarily removed in previous PR). * Addresses some minor TODO comments left over from previous PRs.

view details

Brian Vaughn

commit sha fe2e311db5db1ae3ed2b1263ac577438e45bb204

Add persistent, Static subtreeTag This allows certain concepts to persist without recalculting them, e.g. whether a subtree contains passive effects or portals. This helps for cases like nested unmounts that contain deep passive effects.

view details

Brian Vaughn

commit sha 2876d61f583e3619db0cfd99a4628049b7bd88cf

Add static effectTag bit for passive effect cleanup after unmount

view details

Brian Vaughn

commit sha 0c59d069aae3c7d0654344cb10ae83c5c9cc3b24

Added gate to SchedulingProfiler test

view details

Brian Vaughn

commit sha 7d618ede1794700c287552a1f5e816e6f2586a43

Defer Fiber field clenaup to passive phase for all but return pointer

view details

push time in 7 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitNestedUnmounts( }  function detachFiberMutation(fiber: Fiber) {-  // Cut off the return pointers to disconnect it from the tree. Ideally, we-  // should clear the child pointer of the parent alternate to let this+  // Cut off the return pointers to disconnect it from the tree.+  // Note that we can't clear child or sibling pointers yet,+  // because they may be required for passive effects.+  // These pointers will be cleared in a separate pass.+  // Ideally, we should clear the child pointer of the parent alternate to let this

That being said, I believe we can safely defer everything except the return pointer cleanup to the passive effects phase which would be slightly better. I'll do that...

bvaughn

comment created time in 7 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitNestedUnmounts( }  function detachFiberMutation(fiber: Fiber) {-  // Cut off the return pointers to disconnect it from the tree. Ideally, we-  // should clear the child pointer of the parent alternate to let this+  // Cut off the return pointers to disconnect it from the tree.+  // Note that we can't clear child or sibling pointers yet,+  // because they may be required for passive effects.+  // These pointers will be cleared in a separate pass.+  // Ideally, we should clear the child pointer of the parent alternate to let this

If we don't clear return during layout, it would break a few things:

  • The unmounted state warning
  • How events bubble for the new API Dominic has been working on

That being said, it would fix a limitation of the Profiler API when it comes to measuring time spent in passive effect cleanup. That time currently doesn't get reported during an unmount (either a regular unmount or because of an error boundary) because the return pointer has been detached previously and so it can't bubble up to the nearest Profiler node. If we deferred detaching it until after passive, that time would be reported which is a plus.

Not sure if there would be a viable workaround for the event case, but for the unmounted state warning- we could walk the return path and look for Fibers with Deletion effects scheduled, except that this would have false positives for deletions that were scheduled and not committed so that's a bummer.

bvaughn

comment created time in 7 days

push eventbvaughn/react

Brian Vaughn

commit sha a8077846b629717e895496aefbc875025c824e95

Added gate to SchedulingProfiler test

view details

push time in 7 days

push eventbvaughn/react

Ricky

commit sha 52c51462744ac6a9437d64ae84fcd94eacbb8aa8

Add SchedulerHostConfig fork for post task (#19470)

view details

Brian Vaughn

commit sha 5227a37868c4bf3133ba5f2b3b39cac9175d7ea9

Add "unstable_" prefix to experimental mutable source APIs (#19472) * Add "unstbale_" prefix to mutable source APIs * DebugHooks no longer calls useMutableSource() on init This was causing an observable behavioral difference between experimental DEV and PROD builds. We don't initialize stack position for other composite hooks (e.g. useDeferredValue, useTransition, useOpaqueIdentifier). If we did, it would cause the same obesrvable behavioral difference.

view details

Brian Vaughn

commit sha d45b066bcaf4f73b42c14c971b153b7bf25d309a

Effects list refactor continued: passive effects traversal * Adds new Passive subtree tag value. * Bubbles passive flag to ancestors in the case of an unmount. * Adds recursive traversal for passive effects (mounts and unmounts). * Removes pendingPassiveHookEffectsMount and pendingPassiveHookEffectsUnmount arrays from work loop. * Re-adds sibling and child pointer detaching (temporarily removed in previous PR). * Addresses some minor TODO comments left over from previous PRs.

view details

Brian Vaughn

commit sha c45bc43ffd80717f8c079b485695b875581dcdb1

Add persistent, Static subtreeTag This allows certain concepts to persist without recalculting them, e.g. whether a subtree contains passive effects or portals. This helps for cases like nested unmounts that contain deep passive effects.

view details

Brian Vaughn

commit sha 15288ed5adf7f9456ccb54e10c5e35643b059722

Add static effectTag bit for passive effect cleanup after unmount

view details

Brian Vaughn

commit sha 070eba08db28556aba4f6bf70a6e27a0e32e2b45

Moved resetChildLanes into complete work This enabled us to remove a few hot path tag-type checks, but did not otherwise change any functionality.

view details

push time in 7 days

pull request commentfacebook/react

Effects list refactor continued: passive effects traversal

@acdlite @lunaruan I think 15288ed is the change we discussed earlier.

bvaughn

comment created time in 7 days

push eventbvaughn/react

Brian Vaughn

commit sha 15288ed5adf7f9456ccb54e10c5e35643b059722

Add static effectTag bit for passive effect cleanup after unmount

view details

push time in 7 days

push eventbvaughn/react

Ricky

commit sha 52c51462744ac6a9437d64ae84fcd94eacbb8aa8

Add SchedulerHostConfig fork for post task (#19470)

view details

Brian Vaughn

commit sha 5227a37868c4bf3133ba5f2b3b39cac9175d7ea9

Add "unstable_" prefix to experimental mutable source APIs (#19472) * Add "unstbale_" prefix to mutable source APIs * DebugHooks no longer calls useMutableSource() on init This was causing an observable behavioral difference between experimental DEV and PROD builds. We don't initialize stack position for other composite hooks (e.g. useDeferredValue, useTransition, useOpaqueIdentifier). If we did, it would cause the same obesrvable behavioral difference.

view details

Brian Vaughn

commit sha d45b066bcaf4f73b42c14c971b153b7bf25d309a

Effects list refactor continued: passive effects traversal * Adds new Passive subtree tag value. * Bubbles passive flag to ancestors in the case of an unmount. * Adds recursive traversal for passive effects (mounts and unmounts). * Removes pendingPassiveHookEffectsMount and pendingPassiveHookEffectsUnmount arrays from work loop. * Re-adds sibling and child pointer detaching (temporarily removed in previous PR). * Addresses some minor TODO comments left over from previous PRs.

view details

Brian Vaughn

commit sha c45bc43ffd80717f8c079b485695b875581dcdb1

Add persistent, Static subtreeTag This allows certain concepts to persist without recalculting them, e.g. whether a subtree contains passive effects or portals. This helps for cases like nested unmounts that contain deep passive effects.

view details

Brian Vaughn

commit sha 5b6bc9d4de6d9a213d00ead4be6a3cce226734f0

Add static effectTag bit for passive effect cleanup after unmount

view details

push time in 7 days

Pull request review commentfacebook/react

Effects list refactor continued: passive effects traversal

 function commitUnmount(             const {destroy, tag} = effect;             if (destroy !== undefined) {               if ((tag & HookPassive) !== NoHookEffect) {-                enqueuePendingPassiveHookEffectUnmount(current, effect);+                effect.tag |= HookHasEffect;++                // subtreeTags bubble in resetChildLanes which doens't get called for unmounted subtrees.+                // So in the case of unmounts, we need to bubble passive effects explicitly.

We talked out of band and a summary is:

  • We should add this new static bit to effectTag rather than subtreeTag.
  • The static bit will then bubble to subtreeTag during complete phase for things that are still mounted.
bvaughn

comment created time in 7 days

pull request commentMLH-Fellowship/scheduling-profiler-prototype

Add Chromium flamechart colors

@taneliang I based the current coloring off of the new Mozilla profiler because I like its UI better, but I'm not opposed to trying different ones. Show me? :)

taneliang

comment created time in 7 days

delete branch bvaughn/react

delete branch : unstable_mutable_source

delete time in 8 days

push eventfacebook/react

Brian Vaughn

commit sha 5227a37868c4bf3133ba5f2b3b39cac9175d7ea9

Add "unstable_" prefix to experimental mutable source APIs (#19472) * Add "unstbale_" prefix to mutable source APIs * DebugHooks no longer calls useMutableSource() on init This was causing an observable behavioral difference between experimental DEV and PROD builds. We don't initialize stack position for other composite hooks (e.g. useDeferredValue, useTransition, useOpaqueIdentifier). If we did, it would cause the same obesrvable behavioral difference.

view details

push time in 8 days

more