profile
viewpoint
Sebastian Markbåge sebmarkbage Facebook San Francisco http://blog.calyptus.eu/

reactjs/rfcs 2654

RFCs for changes to React

sebmarkbage/art 898

Retained mode vector drawing API designed for multiple output modes. There's also a built-in SVG parser.

sebmarkbage/ecmascript-asset-references 211

Proposal to ECMAScript to add first-class location references relative to a module

sebmarkbage/ecmascript-generator-expression 93

Proposal for do Generator Expressions in ECMAScript. Work in progress. Edit Add topics

sebmarkbage/ecmascript-undefined-propagation 79

ECMAScript proposal to relax the rules to return `undefined` for property access on `null` or `undefined` instead of throwing.

sebmarkbage/ecmascript-shallow-equal 47

A proposal for ECMAScript for Object.shallowEqual.

sebmarkbage/art-illustrator 16

This project provides Adobe Illustrator ExtendScripts to export graphics to ART Script files.

sebmarkbage/blink-devtools 11

This is an unofficial mirror of the Blink Developer Tools.

pull request commentreactjs/rfcs

RFC: Context selectors

A couple of base line things to consider here before we continue:

  • Previous attempts were measuring a React implementation that isn't optimized for this case. We're doing work to simplifying a lot of these steps as the research has settled on which Concurrent Mode features we actually need compared to what was theoretical. A few things we can do is for example change the render loop when a parent bails out to search down the tree, find work and only lazily clone the parent tree if these actually cause new work. This would help the setState case in the same way as context traversal would. Because right now you could end up traversing down a tree deeply only to find out that the setState didn't lead to a new change. This has the same negative effects so it would be better to solve this generically rather than specifically for Context. This also helps save bytes.

  • Redux has a different use case than useReducer + Context. Redux has more work to do before entering React but some of that work is similar to what React has to do. So there's some duplication. It's also possible to avoid the call. For useReducer you need to enter React's render, and that's important for the semantics of the priorities. So I prefer to compare the useReducer + Context performance to itself rather than comparing an early bailout in Redux.

  • When we talk numbers it's very important to talk about the improvement to lower bounds and upper bounds. Improving a very short update is never going to be a priority if that makes large updates slower. Going from 1 second to 10 second is absolutely unacceptable but going from 1ms to 10ms is perfectly fine. So making rerenders that affect a small part of the tree faster than what is observable isn't a goal, but making medium size updates that are observable are important.

  • I suspect that a lot of these tradeoffs play out differently in sync mode vs concurrent mode. In sync mode you may end up with a lot of scans due to a single sequence of events having to do one render per event where as in concurrent mode they would be batched into a single render and therefore have fewer passes. So the tradeoffs are different. You might see more things actually updating in a single render pass as a result so you end up wasting less.

That said, I think a lot of the lazy optimizations make sense but not just for context but the whole algorithm. There's a lot of value in sharing this.

gnoff

comment created time in 4 days

pull request commentreactjs/rfcs

useMutableSource

This is kind of a nit but an interesting observation.

To validate the idea for more general purposes I was thinking about how it can deal with truly multi-threading. This can already surface given that some browser APIs access state shared by other threads. E.g. Date.now(), isInputPending(), SharedArrayBuffer. (Another variant is if a mutation happens during getVersion or getSnapshot calls.)

This can cause getVersion and getSnapshot to read inconsistent values. getSnapshot could even be internally inconsistent if it's reading a mutable source.

To run getSnapshot on a mutable source, you really need a thread lock around which comes with some perf concerns.

However, if the backing store is a state atom model (e.g. Redux) then you should only need an atomic operation.

You could get this property if you just used the value returned by getVersion as the snapshot. However, to let React know that's what you're doing to avoid a lock, we'd know for sure that's what was happening. One way to ensure this is to make getSnapshot return the result of getVersion by default. I.e. getSnapshot is optional.

const locationSource = createMutableSource(window, () => window.location.href);

const subscribeToLocation = (window, callback) => {
  window.addEventListener("popstate", callback);
  return () => window.removeEventListener("popstate", callback);
};

function Example() {
  const href = useMutableSource(locationSource, subscribeToLocation);
  // We can't use the path name from the location object in this case, but at least this makes it
  // an atomic operation.
  const pathName = useMemo(() => parsePathName(href), [href]);

  // ...
}
bvaughn

comment created time in 4 days

Pull request review commentfacebook/react

useMutableSource hook

+/**+ * 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.+ *+ * @flow+ */++import type {ExpirationTime} from 'react-reconciler/src/ReactFiberExpirationTime';+import type {FiberRoot} from 'react-reconciler/src/ReactFiberRoot';++// Mutable source version can be anything (e.g. number, string, immutable data structure)+// so long as it changes every time any part of the source changes.+export type Version = $NonMaybeType<mixed>;++// Tracks expiration time for all mutable sources with pending updates.+// Used to determine if a source is safe to read during updates.+// If there are no entries in this map for a given source,+// or if the current render’s expiration time is ≤ this value,+// it is safe to read from the source without tearing.+export type MutableSourcePendingUpdateMap = Map<+  MutableSource<any>,+  ExpirationTime,+>;++type MutableSourceConfig = {|+  getVersion: () => Version,+|};++export type MutableSource<Source: $NonMaybeType<mixed>> = {|+  _source: Source,++  _getVersion: () => Version,++  // Tracks the version of this source at the time it was most recently read.+  // Used to determine if a source is safe to read from before it has been subscribed to.+  // Version number is only used during mount,+  // since the mechanism for determining safety after subscription is expiration time.+  //+  // As a workaround to support multiple concurrent renderers,+  // we categorize some renderers as primary and others as secondary.+  // We only expect there to be two concurrent renderers at most:+  // React Native (primary) and Fabric (secondary);+  // React DOM (primary) and React ART (secondary).+  // Secondary renderers store their context values on separate fields.+  // We use the same approach for Context.+  _workInProgressVersionPrimary: null | Version,+  _workInProgressVersionSecondary: null | Version,+|};++export type MutableSourceHookConfig<Source: $NonMaybeType<mixed>, Snapshot> = {|+  getSnapshot: (source: Source) => Snapshot,+  subscribe: (source: Source, callback: Function) => () => void,+|};++// Work in progress version numbers only apply to a single render,+// and should be reset before starting a new render.+// This tracks which mutable sources need to be reset after a render.+let workInProgressPrimarySources: Array<MutableSource<any>> = [];+let workInProgressSecondarySources: Array<MutableSource<any>> = [];++export function createMutableSource<Source: $NonMaybeType<mixed>>(

The use of shared is a last resort and not best practice. In fact, half of the files in there shouldn't be there. Including FeatureFlags that are starting to cause problems.

Even the Symbols one is causing problems because the code size duplication is quite significant. We were talking about moving them to be exports from Isomorphic instead. So I think I might do a pass trying to get rid of as much as possible from /shared/.

This file certainly shouldn't be in shared since createMutableSource and the type signatures should just move there. In terms of many files, I think we should start moving everything in isomorphic to the same file to be more careful about growing that file. It's unnecessarily big for things that can't be made lazy.

bvaughn

comment created time in 4 days

Pull request review commentfacebook/react

useMutableSource hook

 function commitHookEffectListMount(tag: number, finishedWork: Fiber) {       if ((effect.tag & tag) === tag) {         // Mount         const create = effect.create;-        effect.destroy = create();+        if (typeof create === 'function') {

I don't think we should make this a variable type unnecessarily. The VM should be able to check that the "effect" object has a consistent type. The appropriate thing here is probably a bigger refactor but it's not worth introducing this discrepancy for this hook (and potentially making all other effects slower).

I'd prefer that we set this to an empty function in this implementation if we need to rather.

bvaughn

comment created time in 4 days

Pull request review commentfacebook/react

useMutableSource hook

 function useResponder(responder, props): ReactEventResponderListener<any, any> {   }; } +function useMutableSource<Source, Snapshot>(+  source: MutableSource<Source>,+  config: MutableSourceHookConfig<Source, Snapshot>,+): Snapshot {+  resolveCurrentlyRenderingComponent();+  const getSnapshot = config.getSnapshot;+  return getSnapshot(source._source);

Hm. I think this is legit even in the existing server renderer. You can use a shared in memory cache that changes. E.g. another request might be invalidating it at the same time you're rendering.

I'm just not sure how to solve it since it requires a second pass. I think the best we could do is trigger Suspense boundaries and fallback to client rendering. That might be the TODO.

bvaughn

comment created time in 4 days

Pull request review commentfacebook/react

useMutableSource hook

 let renderExpirationTime: ExpirationTime = NoWork; // the work-in-progress hook. let currentlyRenderingFiber: Fiber = (null: any); +// The work-in-progress root fiber.+// Used by mutable source hook to detect tears from pending updates.+let currentlyRenderingRoot: FiberRoot = (null: any);

The other functions are able to be executed mostly standalone and operate on a single Fiber. Passing the argument alone ensures that we do that.

I've been thinking about using a few synchronous passes a couple of times that don't change the workInProgressFiber in the work loop but instead calls begin/complete in its own loop. E.g. for computing exit transition states with different contexts. It would be nice to preserve that property.

With resuming or even smaller similar optimizations (like reusing completed suspended trees) it'll be very important that we don't accidentally use global state since a render of a subtree from one pass needs to be able to be reused in another. I think for gesture calculation that will also be a problem.

This whole approach breaks that rule but that's inherent to this special problem space and not general. We think that we can find a suitable workaround for this one special case.

However, in general we should never operate on the root in begin/complete. That's why I don't think we should be passing the root down through begin since it'll encourage us to use it in more places but this is the one legit case and even this one is on the edge.

Passing lots of unused variables on the stack also has perf implications.

bvaughn

comment created time in 4 days

pull request commentreactjs/rfcs

RFC: Context selectors

I'm pretty convinced that the big problem we see today doesn't have to do with the propagation mechanism of the Context. The main problem that happens in practice is that we end up rendering beyond the component reading the Context because that component creates a new object (e.g. JSX element) that isn't referentially identical to the one before. So this would be solved if everything was auto-memoized.

However, since we're probably not going to have predictable auto-memoization in a reasonable time frame. It's probably better to introduce this as a stop-gap.

The nice thing is that this could be implemented either with the lazy propagation, or as a late bailout which would be closer to the final variant.

Regardless, in terms of API, I think we should implement this. I think we can just use the second argument to useContext and get rid of changed bits though.

gnoff

comment created time in 4 days

pull request commentreactjs/rfcs

useMutableSource

What do you think about dropping the names and wrapper config objects? None of the other built in hooks APIs have these (yet). It’s kind of nice for minification purposes but also avoids an extra object wrapper which would be nice. Especially in a compiler optimized form of useCallback memoization.

It also allows only the subscription callback to be hoisted. Eg if you have a subscription on the whole store but you need the closure only to get some of the data out in the snapshot.

const reduxSource = createMutableSource(store, () => reduxStore.getState());

function Example() {
  const getSnapshot = useCallback(store => store.getState(), [reduxSource]);
  const subscribe = useCallback((store, callback) => store.subscribe(callback)), [reduxSource]);
  const state = useMutableSource(reduxSource, getSnapshot, subscribe);

  // ...
}
bvaughn

comment created time in 4 days

Pull request review commentfacebook/react

Add Auto Import to Babel Plugin

 var div = React.jsx(Component, Object.assign({}, props, { })); `; +exports[`transform react to jsx auto import can specify source 1`] = `+import * as _foobar from "foobar";++var x = _foobar.jsx("div", {+  children: _foobar.jsx("span", {})+});+`;++exports[`transform react to jsx auto import default 1`] = `+import _default from "react";++var x = _default.jsx(_default.Fragment, {+  children: _default.jsxs("div", {+    children: [_default.jsx("div", {}, "1"), _default.jsx("div", {+      meow: "wolf"+    }, "2"), _default.jsx("div", {}, "3"), _default.createElement("div", Object.assign({}, props, {+      key: "4"+    }))]+  })+});+`;++exports[`transform react to jsx auto import in dev 1`] = `+import { createElement as _createElement } from "react";+import { jsxDEV as _jsxDEV } from "react";+import { Fragment as _Fragment } from "react";+var _jsxFileName = "";++var x = _jsxDEV(_Fragment, {+  children: _jsxDEV("div", {+    children: [_jsxDEV("div", {}, "1", false, {+      fileName: _jsxFileName,+      lineNumber: 4+    }, this), _jsxDEV("div", {+      meow: "wolf"+    }, "2", false, {+      fileName: _jsxFileName,+      lineNumber: 5+    }, this), _jsxDEV("div", {}, "3", false, {+      fileName: _jsxFileName,+      lineNumber: 6+    }, this), _createElement("div", Object.assign({}, props, {+      key: "4",+      __source: {+        fileName: _jsxFileName,+        lineNumber: 7+      },+      __self: this+    }))]+  }, undefined, true, {+    fileName: _jsxFileName,+    lineNumber: 3+  }, this)+}, undefined, false);+`;++exports[`transform react to jsx auto import named exports 1`] = `+import { createElement as _createElement } from "react";+import { jsx as _jsx } from "react";+import { jsxs as _jsxs } from "react";+import { Fragment as _Fragment } from "react";++var x = _jsx(_Fragment, {+  children: _jsxs("div", {+    children: [_jsx("div", {}, "1"), _jsx("div", {+      meow: "wolf"+    }, "2"), _jsx("div", {}, "3"), _createElement("div", Object.assign({}, props, {+      key: "4"+    }))]+  })+});+`;++exports[`transform react to jsx auto import namespace 1`] = `+import * as _react from "react";++var x = _react.jsx(_react.Fragment, {

Yea I tested it as well and came to the same conclusion. So we’re not doing this.

The key is that as long as the VM optimizes for storing methods on the object hidden class it’s guaranteed to be worse once you use more than one method.

What I’m wondering next is if it would be worth while having React be a global property.

lunaruan

comment created time in 4 days

pull request commentreactjs/rfcs

useMutableSource

This has nice performance characteristics. If you have many little subscriptions in a tree that need to add themselves to a Map and allocate space, you pay for that post-commit by using a passive effect. So this cost is paid in a non-critical path.

There's some cost paid for creating the config (especially with useMemo) and enqueueing the effect. This is within the realm of being optimizable in components though. In the cases that the config can be hoisted this has very nice performance characteristics to get to a full render pass and paint it. Then you pay for it afterwards to set things up.

I think that's the key benefit of a getVersion vs subscribing in render.

bvaughn

comment created time in 5 days

Pull request review commentfacebook/react

Re-throw errors thrown by the renderer at the root in the complete phase

 function handleError(root, thrownValue) {         // boundary.         workInProgressRootExitStatus = RootFatalErrored;         workInProgressRootFatalError = thrownValue;+        workInProgress = null;

Ah. Subtle. A common would be nice but not required.

acdlite

comment created time in 5 days

pull request commentfacebook/react

Ship React.jsx and React.jsxDEV

This warning is enabled in jsx:

https://github.com/facebook/react/blob/f2fd484afdee1e4e25ee453bb7a544fa0558d172/packages/react/src/ReactElementValidator.js#L371-L373

This means that upgrading to future Babel plugins would enable this warning. Should we wait to enable this until later so it becomes part of a React upgrade instead of a Babel upgrade?

lunaruan

comment created time in 6 days

Pull request review commentfacebook/react

Add Auto Import to Babel Plugin

 module.exports = function(babel) {     },   }); +  const createIdentifierName = (+    path,+    autoImport,+    shouldCacheImportFns,+    name,+    importName,+  ) => {+    if (autoImport === IMPORT_TYPES.none) {+      return `React.${name}`;+    } else if (+      autoImport === IMPORT_TYPES.namedExports ||+      shouldCacheImportFns+    ) {+      const identifierName = `${importName[name]}`;+      return identifierName;+    } else {+      return `${importName}.${name}`;+    }+  };++  const getRequirePath = (path, name, source) => {+    const targetPath = path.get('body').filter(child => {+      const {node} = child;++      return (+        t.isVariableDeclaration(node) &&+        node.declarations.length === 1 &&+        t.isVariableDeclarator(node.declarations[0]) &&+        t.isIdentifier(node.declarations[0].id) &&+        node.declarations[0].id.name === name &&+        t.isCallExpression(node.declarations[0].init) &&+        t.isIdentifier(node.declarations[0].init.callee) &&+        node.declarations[0].init.callee.name === 'require' &&+        node.declarations[0].init.arguments.length === 1 &&+        t.isStringLiteral(node.declarations[0].init.arguments[0]) &&+        node.declarations[0].init.arguments[0].value === source+      );+    });++    if (targetPath.length !== 1) {+      // wtf throw error+    }++    return targetPath[0];+  };++  const getDefaultImportPath = (path, name, source) => {+    const targetPath = path.get('body').filter(child => {+      const {node} = child;++      return (+        t.isImportDeclaration(node) &&+        node.specifiers.length === 1 &&+        t.isImportDefaultSpecifier(node.specifiers[0]) &&+        t.isIdentifier(node.specifiers[0].local) &&+        node.specifiers[0].local.name === name &&+        t.isStringLiteral(node.source) &&+        node.source.value === source+      );+    });++    return targetPath[0];+  };++  const getNamespaceImportPath = (path, name, source) => {+    const targetPath = path.get('body').filter(child => {+      const {node} = child;+      return (+        t.isImportDeclaration(node) &&+        node.specifiers.length === 1 &&+        t.isImportNamespaceSpecifier(node.specifiers[0]) &&+        t.isIdentifier(node.specifiers[0].local) &&+        node.specifiers[0].local.name === name &&+        t.isStringLiteral(node.source) &&+        node.source.value === source+      );+    });++    return targetPath[0];+  };++  const getNamedExportImportPath = (path, name, importedName, source) => {+    const targetPath = path.get('body').filter(child => {+      const {node} = child;+      return (+        t.isImportDeclaration(node) &&+        node.specifiers.length === 1 &&+        t.isImportSpecifier(node.specifiers[0]) &&+        t.isIdentifier(node.specifiers[0].local) &&+        node.specifiers[0].local.name === name &&+        t.isIdentifier(node.specifiers[0].imported) &&+        node.specifiers[0].imported.name === importedName &&+        t.isStringLiteral(node.source) &&+        node.source.value === source+      );+    });++    return targetPath[0];+  };++  function getImportNames(parentPath, state) {+    const imports = {};+    parentPath.traverse({+      JSXElement(path) {+        if (shouldUseCreateElement(path, t)) {+          imports.createElement = true;+        } else if (path.node.children.length > 1) {+          const importName = state.development ? 'jsxDEV' : 'jsxs';+          imports[importName] = true;+        } else {+          const importName = state.development ? 'jsxDEV' : 'jsx';+          imports[importName] = true;+        }+      },++      JSXFragment(path) {+        imports.Fragment = true;+      },+    });+    return imports;+  }++  function shouldAddAutoImports(parentPath) {+    let shouldAdd = false;+    parentPath.traverse({+      JSXElement(path) {+        shouldAdd = true;+        path.stop();+      },++      JSXFragment(path) {+        shouldAdd = true;+        path.stop();+      },+    });++    return shouldAdd;+  }++  function addAutoImports(path, state) {+    if (state.autoImport === IMPORT_TYPES.none || !shouldAddAutoImports(path)) {+      return;+    }++    if (IMPORT_TYPES[state.autoImport] === undefined) {+      throw path.buildCodeFrameError(+        'autoImport must be one of the following: ' ++          Object.keys(IMPORT_TYPES).join(', '),+      );+    }+    if (state.useCreateElement) {+      throw path.buildCodeFrameError(+        'autoImport cannot be used with createElement. Consider setting ' ++          '`useCreateElement` to `false` to use the new `jsx` function instead',+      );+    }+    if (state.autoImport === IMPORT_TYPES.require && isModule(path)) {+      throw path.buildCodeFrameError(+        'Babel `sourceType` must be set to `script` for autoImport ' ++          'to use `require` syntax. See Babel `sourceType` for details.',+      );+    }+    if (state.autoImport !== IMPORT_TYPES.require && !isModule(path)) {+      throw path.buildCodeFrameError(+        'Babel `sourceType` must be set to `module` for autoImport to use `' ++          state.autoImport ++          '` syntax. See Babel `sourceType` for details.',+      );+    }++    // import {jsx} from "react";+    // import {createElement} from "react";+    if (state.autoImport === IMPORT_TYPES.namedExports) {+      const imports = getImportNames(path, state);+      const importMap = {};++      Object.keys(imports).forEach(importName => {+        importMap[importName] = addNamed(path, importName, state.source).name;+      });++      if (state.shouldCacheImportFns) {+        Object.keys(importMap).forEach(importName => {+          const importPath = getNamedExportImportPath(+            path,+            importMap[importName],+            importName,+            state.source,+          );++          const importIdentifier = path.scope.generateUidIdentifier(importName);+          importPath.insertAfter(+            t.variableDeclaration('var', [+              t.variableDeclarator(+                importIdentifier,+                t.identifier(importMap[importName]),+              ),+            ]),+          );+          importMap[importName] = importIdentifier.name;+        });+      }++      return importMap;+    }++    // add import to file and get the import name+    let name;+    if (state.autoImport === IMPORT_TYPES.require) {+      // var _react = require("react");+      name = addNamespace(path, state.source, {+        importedInterop: 'uncompiled',+      }).name;+    } else if (state.autoImport === IMPORT_TYPES.namespace) {+      // import * as _react from "react";+      name = addNamespace(path, state.source).name;+    } else if (state.autoImport === IMPORT_TYPES.defaultExport) {+      // import _default from "react";+      name = addDefault(path, state.source).name;+    }++    // cache react function names as variables at the top of the file+    if (state.shouldCacheImportFns) {+      // var _jsx = _react.jsx;+      // var _createElement = _react.createElement;+      const imports = getImportNames(path, state);+      let importPath;+      let importMap = {};+      if (state.autoImport === IMPORT_TYPES.require) {+        importPath = getRequirePath(path, name, state.source);+      } else if (state.autoImport === IMPORT_TYPES.namespace) {+        importPath = getNamespaceImportPath(path, name, state.source);+      } else if (state.autoImport === IMPORT_TYPES.defaultExport) {+        importPath = getDefaultImportPath(path, name, state.source);+      }++      Object.keys(imports).forEach(importName => {+        const importIdentifier = path.scope.generateUidIdentifier(importName);+        importPath.insertAfter(+          t.variableDeclaration('var', [+            t.variableDeclarator(+              importIdentifier,+              t.memberExpression(t.identifier(name), t.identifier(importName)),+            ),+          ]),+        );+        importMap[importName] = importIdentifier.name;+      });+      return importMap;+    } else {+      // don't cache react function names+      return name;+    }+  }+   visitor.Program = {     enter(path, state) {+      let autoImport = state.opts.autoImport || 'none';+      let source = state.opts.importSource || 'react';+      const CACHE_IMPORT_FNS = state.opts.shouldCacheImportFns || false;+      const {file} = state;++      if (file.ast.comments) {+        for (const comment of (file.ast.comments: Array<Object>)) {

This is not a Flow file. Remove the annotation. Also, let's just use for (let i = 0; ... loop to avoid the lint problem.

lunaruan

comment created time in 6 days

PR opened facebook/react

Reviewers
Change string refs in function component message

This should refer to string refs specifically. The forwardRef part doesn't make any sense in this case. I think this was just an oversight.

+6 -4

0 comment

3 changed files

pr created time in 6 days

create barnchsebmarkbage/react

branch : fixstringrefwarning

created branch time in 6 days

Pull request review commentfacebook/react

Re-throw errors thrown by the renderer at the root in the complete phase

 function handleError(root, thrownValue) {         // boundary.         workInProgressRootExitStatus = RootFatalErrored;         workInProgressRootFatalError = thrownValue;+        workInProgress = null;

What is happening here? Why does this work?

acdlite

comment created time in 6 days

pull request commentfacebook/react

Re-throw errors thrown by the renderer at the root in the complete phase

Your description doesn’t describe what this fix does?

acdlite

comment created time in 6 days

Pull request review commentfacebook/react

Bugfix: Expired partial tree infinite loops

 function workLoopSync() {   } } +function renderRootConcurrent(root, expirationTime) {

Just to be clear, this is only called once so it didn't technically need to be extracted out, right?

acdlite

comment created time in 7 days

pull request commentzeit/styled-jsx

Fix JSXStyle renders styles too late

I was looking at this code trying to figure out some concurrent mode compatibility issues. This fix is not concurrent mode safe (for example, it could remove things before the new changes have committed).

I was able to repro in React by simply adding a previous sibling like this (the codesandbox is broken for me right now so I can't fork it):

<div ref={n => { if (n) n.clientWidth; }} />

This forces a layout. This seems to trick Chrome into treating this as already mounted. Any subsequent mutations then cause a CSS transition to happen. What happens in this case is this: 1) React adds the DOM nodes to the document. 2) Something reads layout in a different life-cycle/effect. 3) React fires JSXStyle's life-cycle which then inserts the style sheet into the document. This causes Chrome to think this is an update to an already visible component and triggers a transition.

Now even if this was fixed in browsers, this sequencing would still leave the second step to potentially read the wrong layout.

We originally planned to add something like useMutationEffect which is what I think @bvaughn was referring to. Since then we decided not to do this. However, the alternative https://github.com/reactjs/rfcs/pull/96 would not actually be sufficient for this case.

So this is missing a good solution atm.

giuseppeg

comment created time in 7 days

Pull request review commentfacebook/react

Move getIsHydrating from FB fork to Experimental flag

 if (exposeConcurrentModeAPIs) {       queueExplicitHydrationTarget(target);     }   };++  ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.getIsHydrating = getIsHydrating;

It's very important that this isn't exposed in the CM release. It won't be needed by the future API neither.

Instead, put it behind a specific feature flag that we won't enable.

gaearon

comment created time in 8 days

Pull request review commentfacebook/react

Portals trigger Suspense boundaries in SSR and only render on client

 describe('ReactDOMServerPartialHydration', () => {     // Now we're hydrated.     expect(ref.current).not.toBe(null);   });++  it('should return fallback during server rendering when Portal has a suspense boundary ', () => {+    const portalContainer = document.createElement('div');

It could but we currently don’t encourage any uses of “is server” conditionals since it’s easy to make mistakes that cause hydration issues as a result.

I was under the impression that we did something that rendered portals internally for SSR. But I think what we actually do is throw before we even get to it.

I think that’s probably the strategy here. Have something that throws before we even get to the code that needs to create a DOM node.

We just don’t have an API to do that.

lunaruan

comment created time in 14 days

Pull request review commentfacebook/react

[WIP] Replay load/error events that happen earlier than commit

+/**+ * 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 ReactDOM;+let ConcurrentMode;++beforeEach(() => {+  React = require('react');+  ConcurrentMode = React.unstable_ConcurrentMode;+  ReactDOM = require('react-dom');+});++describe('ReactDOMImg', () => {+  let container;++  beforeEach(() => {+    // TODO pull this into helper method, reduce repetition.+    // mock the browser APIs which are used in schedule:+    // - requestAnimationFrame should pass the DOMHighResTimeStamp argument+    // - calling 'window.postMessage' should actually fire postmessage handlers+    global.requestAnimationFrame = function(cb) {+      return setTimeout(() => {+        cb(Date.now());+      });+    };+    const originalAddEventListener = global.addEventListener;+    let postMessageCallback;+    global.addEventListener = function(eventName, callback, useCapture) {+      if (eventName === 'message') {+        postMessageCallback = callback;+      } else {+        originalAddEventListener(eventName, callback, useCapture);+      }+    };+    global.postMessage = function(messageKey, targetOrigin) {+      const postMessageEvent = {source: window, data: messageKey};+      if (postMessageCallback) {+        postMessageCallback(postMessageEvent);+      }+    };+    jest.resetModules();+    container = document.createElement('div');+    ReactDOM = require('react-dom');++    document.body.appendChild(container);+  });++  afterEach(() => {+    document.body.removeChild(container);+  });++  it('should trigger load events even if they fire early', () => {+    const onLoadSpy = jest.fn();++    const loadEvent = document.createEvent('Event');+    loadEvent.initEvent('load', false, false);++    // TODO: Write test++    ReactDOM.render(+      <ConcurrentMode>+        <img onLoad={onLoadSpy} />+      </ConcurrentMode>,+    );++    // someHowGetAnImage.dispatchEvent(loadEvent);

@trueadm I think we’ll need to spy on document.createElement by wrapping it so we can detect which img instance React creates.

We can’t use a ref to simulate this because it doesn’t resolve in time.

sebmarkbage

comment created time in 14 days

Pull request review commentfacebook/react

Bugfix: Expired partial tree infinite loops

 function performConcurrentWorkOnRoot(root, didTimeout) {   // event time. The next update will compute a new event time.   currentEventTime = NoWork; -  // Check if the render expired. If so, restart at the current time so that we-  // can finish all the expired work in a single batch. However, we should only-  // do this if we're starting a new tree. If we're in the middle of an existing-  // tree, we'll continue working on that (without yielding) so that the work-  // doesn't get dropped. If there's another expired level after that, we'll hit-  // this path again, at which point we can batch all the subsequent levels-  // together.-  if (didTimeout && workInProgress === null) {-    // Mark the current time as expired to synchronously render all expired work-    // in a single batch.-    const currentTime = requestCurrentTimeForUpdate();-    markRootExpiredAtTime(root, currentTime);+  // Check if the render expired.+  const expirationTime = getNextRootExpirationTimeToWorkOn(root);+  if (didTimeout) {+    if (workInProgressRoot === root && renderExpirationTime !== NoWork) {+      // We're already in the middle of working on this tree. Expire the tree at+      // the already-rendering time so we don't throw out the partial work. If+      // there's another expired level after that, we'll hit the `else` branch+      // of this condition, which will batch together the remaining levels.+      markRootExpiredAtTime(root, renderExpirationTime);

It seems a bit like this make the expired value lie. If we added more places where we check the current time, we would update this to something further out. It's also not clear to me whether when we're actually going to set its to its real value and stop temporarily lying.

Instead of doing this here. Can we just let it set it to the lower expired value like we did before? Then instead check in performSyncWorkOnRoot if it has expired work but the expired work is lower priority then the currently rendering work, first do a pass finishing the currently rendering work and then do a second pass at the expired priority and then possibly a third pass if it errors in the second pass?

https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberWorkLoop.js#L992

acdlite

comment created time in 15 days

pull request commentfacebook/react

[Mock Scheduler] Mimic browser's advanceTime

I think I'd prefer a helper that plays time forward for a certain time with a reasonable amount of flushes in between. It shouldn't just jump to the end as if the CPU was stalled for the whole time. Seems like we'd test very unrealistic scenarios that way. The order might matter.

acdlite

comment created time in 15 days

pull request commentfacebook/react

[Scheduler] shouldYield should always return false from inside an expired task

It seems to me that shouldYield is already way above its budget for such a hot function. I'd expect a single atomic read basically.

It also seems to me that we're getting rid of the notion of "expiration time" in the scheduler since we won't have a timeoutMs option and the native one won't work this way anyway so this doesn't translate.

I think we should just simplify this check to only yield every 5ms regardless if higher pri tasks are scheduled or anything else.

acdlite

comment created time in 15 days

pull request commentfacebook/react

Flush all passive destroy fns before calling create fns

I'm confused about the review process here. The previous PR includes everything from this PR and is merged. https://github.com/facebook/react/pull/17925

bvaughn

comment created time in 15 days

push eventfacebook/react

Sebastian Markbåge

commit sha ace9e8134c3080d86f20097d5ba3369e15a97a83

Simplify Continuous Hydration Targets (#17952) * Simplify Continuous Hydration Targets Let's use a constant priority for this. This helps us avoid restarting a render when switching targets and simplifies the model. The downside is that now we're not down-prioritizing the previous hover target. However, we think that's ok because it'll only do one level too much and then stop. * Add test meant to show why it's tricky to merge both hydration levels Having both levels co-exist works. However, if we deprioritize hydration using a single level, we might deprioritize the wrong thing. This adds a test that catches it if we ever try a naive deprioritization in the future. It also tests that we don't down-prioritize if we're changing the hover in the middle of doing continuous priority work.

view details

push time in 15 days

PR merged facebook/react

Simplify Continuous Hydration Targets CLA Signed React Core Team

Let’s use a constant priority for this. This helps us avoid restarting a render when switching targets and simplifies the model.

The downside is that now we're not down-prioritizing the previous hover target. However, we think that's ok because it'll only do one level too much and then stop.

I also add test meant to show why it's tricky to merge both hydration levels. Having both levels co-exist works. However, if we deprioritize hydration using a single level, we might deprioritize the wrong thing.

It also tests that we don't down-prioritize if we're changing the hover in the middle of doing continuous priority work. We don't do the -1 trick for these and so we don't have to consider that in the new model. Just that we shouldn't start doing that.

+205 -24

4 comments

3 changed files

sebmarkbage

pr closed time in 15 days

PR opened facebook/react

Reviewers
Simplify Continuous Hydration Targets

Lets use a constant priority for this. This helps us avoid restarting a render when switching targets and simplifies the model.

The downside is that now we're not down-prioritizing the previous hover target. However, we think that's ok because it'll only do one level too much and then stop.

I also add test meant to show why it's tricky to merge both hydration levels. Having both levels co-exist works. However, if we deprioritize hydration using a single level, we might deprioritize the wrong thing.

It also tests that we don't down-prioritize if we're changing the hover in the middle of doing continuous priority work. We don't do the -1 trick for these and so we don't have to consider that in the new model. Just that we shouldn't start doing that.

+205 -24

0 comment

3 changed files

pr created time in 18 days

push eventsebmarkbage/react

Sebastian Markbage

commit sha d92aea65d007783994dea175381a0af79cbfa136

Add test meant to show why it's tricky to merge both hydration levels Having both levels co-exist works. However, if we deprioritize hydration using a single level, we might deprioritize the wrong thing. This adds a test that catches it if we ever try a naive deprioritization in the future. It also tests that we don't down-prioritize if we're changing the hover in the middle of doing continuous priority work.

view details

push time in 18 days

create barnchsebmarkbage/react

branch : simplifycontinuoushydration

created branch time in 18 days

pull request commentfacebook/react

Add useOpaqueReference Hook

I originally suggested that this be called useOpaqueReference because it's effectively a "native ref". In React Native, this could be implemented as a native box just like refs to contain the currently referenced native view or value. It's more like a ref than an ID since an ID is just an inefficient way to implement that. The longer name "Reference" was meant to differentiate it a bit from "Ref". It was also to discourage trying to use it as a string somewhere.

However, I think that was a little too clever. The flaw in naming is that it would seem like you could pass it into ref={...} instead of id={...}. That was actually my thinking that you'd pass it to ref={...} on a native view in RN. There are some practical reasons that maybe we shouldn't do that initially though.

So calling it "useOpaqueIdentifier" might be better for now.

I'm a bit concerned that it's too easy to accidentally use this for the wrong purpose (e.g. generate session IDs or seeds) if you only use this API client-side.

I wonder if we should let DEV mode sometimes (randomly?) return a wrapper object with a toString that warns unless it's React that uses it. For client rendering. So that you can't accidentally rely on it being a string when developing.

lunaruan

comment created time in 19 days

push eventfacebook/react

Sebastian Markbåge

commit sha e98797f7b98c827fb691e519bc2c445977a0546c

Fix Event Replaying in Flare by Eagerly Adding Active Listeners (#17933) * Add test of Event Replaying using Flare * Fix Event Replaying in Flare by Eagerly Adding Active Listeners This effectively reverts part of https://github.com/facebook/react/pull/17513

view details

push time in 19 days

PR merged facebook/react

Fix Event Replaying in Flare by Eagerly Adding Active Listeners CLA Signed React Core Team

This effectively reverts part of #17513. I duplicated the event replaying tests to now include Flare too, just like the partial hydration tests already did. The existing tests only covered passive events.

The primary problem is that if there is no active listener attached, then the IS_ACTIVE flag isn't passed into DEPRECATED_dispatchEventForResponderEventSystem. So the replaying of the events, like Press, that normally listen to active events doesn't work.

Replaying doesn't technically need to be active for all events. It's not guaranteed to always work to preventDefault if the code hasn't loaded yet. We could make the listeners attached as passive in the DOM but still pass the IS_ACTIVE flag. That would still "work" but it wouldn't actually work to prevent default.

The problem is that we do use a best-effort attempt to allow preventDefault to work. Most of the time it just works because we are able to synchronously hydrate within the event. That's also how we preventDefault on links to prevent the browser from taking over.

So I think ideally, at least some event should still be attached as active such as submit, click and the key events. We don't have to make them all active. Notably the ones that can cause problems for scroll optimizations and such are the pointer events.

+251 -4

3 comments

2 changed files

sebmarkbage

pr closed time in 19 days

issue commentfacebook/react

Bug: Render-phase update to another root causes an over-rendering loop

It might help the explanation if you consider this:

function Root1() {
  setTimeout(() => _setX(x => x + 1), 0);
  return 'Hello';
}

Assuming there's other work that takes longer than the timeout to complete.

Let's say I'm rendering Root1 at low pri. Then I call _setX on something, it will now be Normal pri. Which is higher pri. So React switches to rendering that and throws out the work it had started. Since we've given up on that. When that completes we try the original render again. This adds another update to the queue at Normal pri which interrupts the work again. This will continue until we increase the expiration time and eventually it starts becoming higher pri than Normal. However, with Andrew's simplification of the model, that won't even happen so it'll just infinitely lock until we pick some time to go synchronous.

Now, on two different roots we have the "option" to keep the original work in progress and continue where we left off so it might accidentally work across roots but it's very fragile if something else happens on the root in the meantime that causes it to interrupt or if you ever merge the roots or we change when we interrupt across roots.

gaearon

comment created time in 19 days

issue commentfacebook/react

Bug: Render-phase update to another root causes an over-rendering loop

I think I can't to close this as "by design" because it's ultimately about if higher pri work on another root is allowed to interrupt this root.

gaearon

comment created time in 19 days

issue commentfacebook/react

[Concurrent] Safely disposing uncommitted objects

@markerikson It can mean slightly different things in different implementation details. For example, it might not need to be in the "commit" phase. We can do it before the commit phase if we know it's going to happen earlier. It always means switching to synchronous rendering though.

It may also mean including more things in the batch than is necessary. E.g. normally React concurrent can work with multiple different pending transitions independently but deopt might group them into one batch.

For example if you have pending transitions loading it might also need to force those to be included into one big batch and force them to show early fallbacks. Such as pending startTransition. This can be an unfortunate experience if forcing fallbacks means hiding existing content while loading but you really just wanted to show a temporary busy spinner.

Another case we haven't built yet but that I'm thinking of is enter/exit animations. It might force the animation to terminate early.

I.e. new features that don't work with synchronous rendering can become disabled if we're forced into synchronous rendering. I think that's the worst part of the deopt. Not actually the rendering performance.

The cases when deopts happen is also an interesting discussion point that varies by implementation details. E.g. the useMutableSource approach is meant to trigger the deopt less often.

FredyC

comment created time in 19 days

push eventsebmarkbage/react

Sebastian Markbage

commit sha 16c213bb3750073b869821bfbba3be5bd822e406

Fix Event Replaying in Flare by Eagerly Adding Active Listeners This effectively reverts part of https://github.com/facebook/react/pull/17513

view details

push time in 20 days

Pull request review commentfacebook/react

Fix Event Replaying in Flare by Eagerly Adding Active Listeners

 describe('ReactDOMServerPartialHydration', () => {     const TestResponder = React.DEPRECATED_createResponder(       'TestEventResponder',       {-        targetEventTypes: ['click'],

This test didn't catch it because apparently this means that it's listening to both active and passive. With the fix, now it actually fires twice which I think is a bug.

sebmarkbage

comment created time in 20 days

push eventsebmarkbage/react

Sebastian Markbage

commit sha 1f4b209e32e00c992e8bd13215aeaf2d1af0f8ee

Change existing tests to listen to active clicks

view details

push time in 20 days

PR opened facebook/react

Reviewers
Fix Event Replaying in Flare by Eagerly Adding Active Listeners

This effectively reverts part of #17513

The primary problem is that if there is no active listener attached, then the IS_ACTIVE flag isn't passed into DEPRECATED_dispatchEventForResponderEventSystem. So the replaying of the events, like Press, that normally listen to active events doesn't work.

Replaying doesn't technically need to be active for all events. It's not guaranteed to always work to preventDefault if the code hasn't loaded yet. We could make the listeners attached as passive in the DOM but still pass the IS_ACTIVE flag. That would still "work" but it wouldn't actually work to prevent default.

The problem is that we do use a best-effort attempt to allow preventDefault to work. Most of the time it just works because we are able to synchronously hydrate within the event. That's also how we preventDefault on links to prevent the browser from taking over.

So I think ideally, at least some event should still be attached as active such as submit, click and the key events. We don't have to make them all active. Notably the ones that can cause problems for scroll optimizations and such are the pointer events.

+256 -0

0 comment

2 changed files

pr created time in 20 days

create barnchsebmarkbage/react

branch : selectivehydrationflare

created branch time in 20 days

issue commentfacebook/react

[Concurrent] Safely disposing uncommitted objects

@FredyC The issue with the subscription based approach is what you do when the subscription fires after we’ve rendered that component but before we’ve committed it onto the screen and we get an update. React can do two things with that information. If React doesn’t immediately stop then the rest of the tree will get the new state and it will be inconsistent and cause tearing problems. If React does restart, it’s worse than the deopt approach because you eagerly throw away work even in cases where you wouldn’t need to. If you restart but keep it non-synchronous rendering you risk keep throwing away the work as you get new updates and you starve which is really bad.

So the subscription solution is actually a much worse deopt or it has the tearing problems.

Or to be precise it would be same performance as without Concurrent mode. That sounds more like a foot gun and not a solution.

Yes, this is indeed a foot gun but there’s not much we can do about that. A mutable state that mutates and then later dispatches a change notification that the state has already changed inherently has this property.

It’s a known property in systems that use threads too. Immutable data lets you about snapshots and allows work to be queued and parallelized.

It comes with some downsides too and it’s a tradeoff. We can’t magically fix that.

The deopt approach is a decent compromise but it does make the story more complicated because you can’t really just say to use the same state for everything since that risks deopting too much. So that makes it a foot gun.

FredyC

comment created time in 20 days

PR closed reactjs/rfcs

Fragment refs RFC CLA Signed

Summary

This is an alternative to findDOMNode.

Basic example

function Foo({children}) {
  let fragmentRef = useRef();
  useEffect(() => {
    let domNodes = fragmentRef.current;
    // ...
  });
  return <Fragment ref={fragmentRef}>{children}</Fragment>;
}

Rendered text

+107 -0

15 comments

1 changed file

sebmarkbage

pr closed time in 22 days

pull request commentreactjs/rfcs

Fragment refs RFC

I'm going to close this RFC out since I'm pretty convinced that this particular implementation is not a good idea.

It doesn't seem super critical as a way to move off findDOMNode. We've found that it is possible to migrate off it with some inconvenience and that's also what we've heard from the community.

I still one or two of the alternatives I mentioned above would be good additions to React as a further convenience that doesn't break encapsulation.

I encourage someone to write up an RFC for each of the alternatives.

sebmarkbage

comment created time in 22 days

pull request commentreactjs/rfcs

Fragment refs RFC

Ok, so I've been researching use cases for findDOMNode or something like Fragment refs - and I don't think this is the right direction.

Taking a deeper look was originally spawned from seeing that fragment refs ends up being quite confusing when it comes to hidden offscreen content (such as prerendered, display locked or hidden due to a suspense boundary). You may end up getting more nodes than you'd expect. More over, it is too tempting to just assume or assert a single child node while not considering multi-node implementations like suspense boundaries.

Encapsulation

The fundamental benefits we get from React's component model is the constraints it enforces so that you can trust how other components behave. E.g. when their life-cycles fire, that they get there input from props/context etc. A parent can know something about what its children does, but a child shouldn't need to know about its parents. It's a critical property that you can refactor children without also refactoring all its uses. That empowers reuse. A key feature of that is being able to change the implementation details by using multiple DOM nodes or wrapping the root component in a Suspense boundary that renders two nodes, or rendering a fragment with one in-place DOM node and one portal. It's also about knowing which properties a parent is allowed to override by accepting explicit props or context to manage those overrides.

Neither findDOMNode nor Fragment refs preserve that quality. Since they give full access to the DOM node. The net effect is that components have to resort to defensive programming. E.g. adding an early wrapper just in case someone drills into the component. It also adds a risk that discourages refactoring such components.

forwardRef

forwardRef doesn't have this problem, as much, since it's an explicit reference that you opt-in to provide a public API that includes a particular DOM node. It also allows the ref to be redirected to a specific place so it can change position during refactors. You can also use named ref props to allow multiple refs.

Unfortunately it also encourages all these components to expose their entire DOM node. It's a reasonable escape hatch but not ideal. Ultimately I think we'll want to move towards not needing this as much. For example, if we had more like first-class focus management or something "scopes" based way of setting focus then components wouldn't need to expose a directly focusable thing.

For now it's a decent workaround though.

Use Cases

Use cases for findDOMNode tends to fall into roughly these categories:

  • Adding additional properties or styles to the child.
  • Adding additional event listeners to the child.
  • Setting focus on the child.
  • Various use cases related to the layout rectangle of a component, such as visibility tracking and scrolling into view.

Adding additiona properties or styles to a child is generally considered bad and not expected since it may collide with the styles already defined in a component. There's generally good intuition about this but it sometimes breaks down. E.g. is it ok to add an outline to a child component's styles when it's focused? What if there's two children in a fragment? Typically when this happens, it's a use case that could be better solved by a wrapper DOM element that takes on the size of the children.

In fact, all these cases can be solved by adding a wrapper DOM node. I'd argue that any use case that couldn't be solved by a wrapper DOM node is not legit since it risks conflicting with the child.

Even for things like event listeners, that can have two at once, this causes problems with unclear invocation order. It's not obvious when an event listener will get attached. E.g. normally a parent's life-cycle fire later so it probably attaches an event listener that is invoked after the child. However, if you call useEffect to attach and remove event listeners, then changes to the deps in the child may cause it to readd a new listener that is now invoked after the parent. You also have to be careful to also call stopImmediatePropagation at the right places. It's just much simpler if the event bubbling model is always related to parent and child.

Therefore, I argue that the default solution to all these problems should be to add a wrapper DOM node.

Why Not Wrapper DOM Elements?

In practice, it can be difficult to add a wrapper Element in the DOM for a number of reasons:

  • There might be perf considerations with having too many DOM nodes in the tree.
  • It can mess with CSS child selectors.
  • It can mess with layout in certain cases such as changing the flex environment.
  • The accessibility tree needs to be considered and aria used properly.
  • If the component is rendered between two special tags such as <tbody /> and <tr /> it can break server rendering because the HTML parser in browsers doesn't allow arbitrary tags to be inserted in this context.

The performance considerations for DOM nodes are real, but keep in mind that we're talking about a small linear increase. It's not exponential. In general when DOM nodes are too many it's because either windowing (virtualization) is not used, or you have too many complex CSS selectors. I'd also expect that the overall effect would be fewer DOM nodes or the same after a while given that reusable components now don't have to use as much defensive coding.

Regarding CSS child selectors, if abused in this way, they have the same encapsulation problems as findDOMNode. I'd argue that they shouldn't be used at all in components system and if used they should be restricted to not drill through the encapsulation. Even outside React, this is what Shadow DOM is for. If this remains a problem, maybe we should consider rendering components into Shadow DOM so that these abuses are not possible.

Defining layout of a wrapper can be tricky. Luckily display: contents is supposed to solve this. It does have some issues with a11y atm skipping over and into them but for the purpose that these are used I believe this can be sufficient. Similarly, the aria tree can be messed with things like grid-row/cell similar to HTML, but I hear "presentation" can be used for these intermediates.

HTML parsing and SSR is a problem that doesn't have an obvious solution in user space right now. The typical solution is to provide an optional tag name that can be provided in the cases this component is used in unusual contexts like a table.

Alternative 1: Reified Fragment Node

I think the ideal would be that the browser adds a first-class fragment tag that solves these issues by default. However, until then it's possible that we could build a polyfill in React. One possible idea is that when you render <fragment> we would create a real DOM element and automatically assign the right styles and a11y rules to it based on its context as to make it transparent.

To solve the HTML parsing problem, React could omit the fragment tags during HTML rendering in contexts where it's not allowed. Then during hydration it could add them back in. This works because when you imperatively append content to the DOM, invalid tags are allowed to exist in the tree.

Alternative 2: Virtual Fragment Node

Some things, like rendering an additional outline around a component, will need a real reified element. However many use cases are not adding additional behavior to the DOM. They're just observing something about the DOM. These typically fall into two categories:

  • Events
  • Bounding Rectangle

Events are pretty easy to do in React since we already have a virtual event system. We can add additional levels and bubble through a fragment just as if it was really in the DOM without reifing it.

<Fragment onClick={e => e.stopPropagation()}>...</Fragment>

The only thing is that, unlike a reified fragment, this wouldn't capture events on text nodes. You'd have to add an explicit wrapper for that.

Similarly, we could measure the client rectangle anytime the children resizes:

<Fragment onResize={boundingRect => ...}>...</Fragment>

When I say "bounding rect" I really mean some abstraction over the whole stacking context. So that you can figure out if it's obscured by other boxes. It might also need to be multiple rectangles when they're not adjacent such as the rectangle of a portal.

The use cases that require the bounding box of the child includes:

  • Determining if a component is visible or scrolled offscreen or obscured.
  • Scrolling a component into view by ensuring that scrolling includes the bounds of the component.
  • Positioning a tooltip next to or near the bounds of the component.
  • Clicking or setting focus in the middle of the component (for testing).

The bounding box is special because it is a primitive contract of a component. Every component ultimately takes up space in the layout of its parent (or zero if it's a portal/modal). This size can be trivially observed, e.g. adding a reified wrapper that sizes itself to its child and then measure that. This type of use case doesn't break encapsulation because you can completely change the implementation (including rendering a canvas even without DOM nodes) as long as it takes up layout somehow.

I think a lot of people have found this intuition given that these are the primary use cases where people use findDOMNode today.

So this is a very specific use case that we could add support for without giving full access to the DOM nodes.

Literally having an onResize callback with ResizeObserver on each child might be way overkill for some of these use cases though so the API should be designed to be implemented more efficiently for occasional measurements. Imperative on-demand measurements are not great since they observe where we currently are and not where we're going (concurrently). So ideally it should only expose sizes during commit (synchronized) life-times.

sebmarkbage

comment created time in 22 days

Pull request review commentfacebook/react

Add different string ref warning when owner and self are different

 function defineRefPropWarningGetter(props, displayName) {   }); } +function warnIfStringRefCannotBeAutoConverted(config) {+  if (__DEV__) {+    if (+      typeof config.ref === 'string' &&+      ReactCurrentOwner.current &&+      config.__self &&+      ReactCurrentOwner.current.stateNode !== config.__self+    ) {+      const componentName = getComponentName(ReactCurrentOwner.current.type);++      if (!didWarnAboutStringRefs[componentName]) {+        console.error(+          'Component "%s" contains the string ref "%s". ' ++            'Support for string refs will be removed in a future major release. ' ++            'This case cannot be automatically converted to an arrow function. ' ++            'We as you to manually fix this case by using useRef() or createRef() instead. ' +

*We ask

lunaruan

comment created time in a month

pull request commentfacebook/react

Remove dynamic GKs for selective/train

@gaearon Marginally for SSR hydration but it turns out to not be the major bottleneck so hard to tell how much.

sebmarkbage

comment created time in a month

push eventfacebook/react

Sebastian Markbåge

commit sha cf7a0c24d4640f07d465c5a67aab2639ddd5d0a9

Remove dynamic GKs for selective/train (#17888) There are shipped/shipping.

view details

push time in a month

PR merged facebook/react

Reviewers
Remove dynamic GKs for selective/train CLA Signed React Core Team

There are shipped/shipping.

+9 -8

4 comments

7 changed files

sebmarkbage

pr closed time in a month

pull request commentreactjs/reactjs.org

Update docs on how to contribute

Ah. I read it in reverse. 😂

necolas

comment created time in a month

Pull request review commentfacebook/react

Add different string ref warning when owner and self are different

 describe('ReactDeprecationWarnings', () => {         '\n    in Component (at **)',     );   });++  it('should not warn when owner and self are the same for string refs', () => {

This says not but the test says it warns. Probably because it's concurrent mode which implies strict.

Try using ReactNoop.renderLegacySyncRoot instead.

lunaruan

comment created time in a month

pull request commentfacebook/react

Make disable context flag dynamic for WWW

Let's discuss this because I'm not sure of a safe way you can use this. I imagine you want to turn it on based on a particular site's flag so a particular tree can't have it, but I don't know if that actually does what you think it does.

gaearon

comment created time in a month

pull request commentreactjs/reactjs.org

Update docs on how to contribute

Is this new content or existing links taken from elsewhere?

necolas

comment created time in a month

PR opened facebook/react

Reviewers
Remove dynamic GKs for selective/train

There are shipped/shipping.

+9 -8

0 comment

7 changed files

pr created time in a month

create barnchsebmarkbage/react

branch : disableflags

created branch time in a month

issue commentfacebook/react

Enable a lint rule not to define after return and fix existing callsites

@M-Izadmehr Great! Thanks for taking a look. I think the first step is to look at our es-lint config and enable a canonical lint rule that already exists upstream.

After that you should be able to open a PR which should fail the lint rule. After that you could do a separate commit that contains the code fixes. I wouldn't start by fixing the code though as that risks rebase issues.

sebmarkbage

comment created time in a month

issue openedfacebook/react

Enable a lint rule not to define after return and fix existing callsites

https://twitter.com/therealyashsriv/status/1219691914523545601

We shouldn't generate code that might cause browser or linting to complain.

https://github.com/facebook/react/blob/0cf22a56a18790ef34c71bef14f64695c0498619/packages/legacy-events/SyntheticEvent.js#L259

It's also just a confusing pattern at best.

created time in a month

push eventfacebook/react

Sebastian Markbåge

commit sha 0c04acaf8955d3f754ec894abfc7586cbf3220dc

Remove FB specific build (#17875) I moved unstable_SuspenseList internally. We don't need the FB build. I plan on also removing the ReactDOM fb specific entry. We shouldn't add any more FB specific internals nor APIs. If they're experimental they should go onto the experimental builds to avoid too many permutations.

view details

push time in a month

PR merged facebook/react

Reviewers
Remove FB specific React isomorphic build CLA Signed React Core Team

I removed unstable_SuspenseList internally. We don't need the FB build. I plan on also removing the ReactDOM fb specific entry. We shouldn't add any more FB specific internals nor APIs. If they're experimental they should go onto the experimental builds to avoid too many permutations.

+0 -29

3 comments

2 changed files

sebmarkbage

pr closed time in a month

pull request commentfacebook/react

Add feature flag around React.createFactory

We should add the warning now then. I'm scared we'll forget it if we leave it.

trueadm

comment created time in a month

PR opened facebook/react

Reviewers
Remove FB specific build

I removed unstable_SuspenseList internally. We don't need the FB build. I plan on also removing the ReactDOM fb specific entry. We shouldn't add any more FB specific internals nor APIs. If they're experimental they should go onto the experimental builds to avoid too many permutations.

+0 -29

0 comment

2 changed files

pr created time in a month

create barnchsebmarkbage/react

branch : rmreactfb

created branch time in a month

pull request commentfacebook/react

Add feature flag around React.createFactory

Before we actually remove this, we’ll need to run a warning for a while. Should we also have a flag to add the warning?

trueadm

comment created time in a month

Pull request review commentfacebook/react

Add different string ref warning when owner and self are different

 function coerceRef(         const componentName = getComponentName(returnFiber.type) || 'Component';         if (!didWarnAboutStringRefs[componentName]) {           if (warnAboutStringRefs) {-            console.error(-              'Component "%s" contains the string ref "%s". Support for string refs ' +-                'will be removed in a future major release. We recommend using ' +-                'useRef() or createRef() instead. ' +-                'Learn more about using refs safely here: ' +-                'https://fb.me/react-strict-mode-string-ref%s',-              componentName,-              mixedRef,-              getStackByFiberInDevAndProd(returnFiber),-            );+            if (+              element._owner &&+              element._self &&+              element._owner.stateNode !== element._self+            ) {+              console.error(

The reason we put the other warning in this file is because it's the only way to know if you're in StrictMode or not.

However, it's a sub-optimal place to put it because the error stack isn't as good. It doesn't point out the exact callsite. Since we don't need to check for StrictMode and can just always warn, you can instead put this in React.jsx and React.createElement so that we get better error stacks.

lunaruan

comment created time in a month

Pull request review commentfacebook/react

Add different string ref warning when owner and self are different

 function coerceRef(         const componentName = getComponentName(returnFiber.type) || 'Component';         if (!didWarnAboutStringRefs[componentName]) {           if (warnAboutStringRefs) {-            console.error(-              'Component "%s" contains the string ref "%s". Support for string refs ' +-                'will be removed in a future major release. We recommend using ' +-                'useRef() or createRef() instead. ' +-                'Learn more about using refs safely here: ' +-                'https://fb.me/react-strict-mode-string-ref%s',-              componentName,-              mixedRef,-              getStackByFiberInDevAndProd(returnFiber),-            );+            if (

The warnAboutStringRefs flag just says to turn on a warning for all string refs, regardless if it's in strict mode or not. That's a very aggressive flag. If you're getting warnings for everything then it's not much better to get a slightly different message (unless filtering but nobody does that).

I think we'll want to add this warning much more aggressively. I.e. not behind a flag at all and outside strict mode.

lunaruan

comment created time in a month

Pull request review commentfacebook/react

Add different string ref warning when owner and self are different

 function coerceRef(         const componentName = getComponentName(returnFiber.type) || 'Component';         if (!didWarnAboutStringRefs[componentName]) {           if (warnAboutStringRefs) {-            console.error(-              'Component "%s" contains the string ref "%s". Support for string refs ' +-                'will be removed in a future major release. We recommend using ' +-                'useRef() or createRef() instead. ' +-                'Learn more about using refs safely here: ' +-                'https://fb.me/react-strict-mode-string-ref%s',-              componentName,-              mixedRef,-              getStackByFiberInDevAndProd(returnFiber),-            );+            if (+              element._owner &&+              element._self &&+              element._owner.stateNode !== element._self+            ) {+              console.error(+                'Owner and self do not match for the string ref "%s". Support for ' +

This doesn't really tell the user anything. They don't know what owner and self not matching means. How about:

Support for string refs will be removed in a future major release. This case cannot be automatically converted to an arrow function. We ask you to manually fixing this case by using useRef() or createRef() instead. Learn more about using refs safely here: https://fb.me/react-strict-mode-string-ref%s

lunaruan

comment created time in a month

issue commentfacebook/react

[Concurrent] Safely disposing uncommitted objects

Re: connecting to an external store.

There are a lot of implementation details of current systems. That might make upgrading hard but that doesn't say whether it's a good idea longer term. I think ultimately this problem bottoms out in reading a mutable value in a newly created component (already mounted components can use snapshots in their state). Depending on if your system also wants to support Suspense this might get conflated with Suspense, see below.

We're working on a new hook for mutable external values that deopts in scenarios where it would produce an inconsistent tree. By doing a full batched render in synchronous mode. It's "safe" and it does allow a tree to commit as one. It's unsatisfactory because it will deopt and produce unfortunate perf characteristics in certain scenarios.

It's related to this thread, because it aims to solve scenarios that I'm not sure has been considered yet. I believe that even if we had a clean up hook, you would still leave unsafe scenarios on the table or at the very least deopt in more cases.

In terms of a mutable store, the subscription fills the purpose of invalidating the render if it changes before commit. Otherwise the useSubscription pattern works. However, you also have to think about whether invalidating the render should also take any higher pri updates with it. It's not just about consistency but whether you merge multiple timelines. Additionally, if you always invalidate a render for changes to the store you risk starving out. I think what you really want is to commit the work you've already done even if it's now stale, as long as that commit is consistent with itself.

That means we should continue rendering until we find a case that is impossible to make consistent and only deopt once we hit that. Because if you use it sparingly you don't have to deopt at all. However, you don't need a subscription to accomplish this because it's not the already rendered component that determines whether the tree is inconsistent, it's the next rendered component that we haven't gotten to yet.

Re: suspense.

I agree that suspense intuitively feels like you should be able to open connections during render. That's what our original demos showed. It seems right. However, there are a few caveats.

One is that even when React does its best to preserve already started work, it's surprisingly easy for rebases, especially long lived ones, to invalidate it. Therefore I believe you still need something like a timeout based cache to solve this regardless of what React does.

Additionally, there are other problems with this approach with regard to when invalidation of data that has already rendered should happen. The solution to that is keeping an object in state that represents the current lifetime like we showed at the latest React Conf. If you use that approach then you also solve the problem of when to invalidate opened requests. When you explicitly cancel the lifetime of the subtree, then you cancel any requests opened by that subtree.

Now, in theory we could do a traversal for most cases but it wouldn't be fool-proof and it would make React slower for everyone. To be convinced of this, we would have to see that the whole system overall that spawns from this actually works well, not just in theory, and that its convenience is worth making React slightly slower for everyone.

FredyC

comment created time in a month

issue commentfacebook/react

Bypass synthetic event system for Web Component events

There's active work on various ways of approaching this but turns out there are a lot of considerations. For example, how coordination of touch events needs to work for complex gestures and how this interacts with the bubbling, how this works with SSR selective hydration, how you add concepts like scheduling priority to events, how you separate discrete events from continuous events in a concurrent environment, how do you deal with "global" events like keystrokes etc. We've had a few failed attempts and we want to get it right.

Now we could add the simple model that just by-passes and leaves those questions unsolved. However, we know that if we just added the simple model, we would want to deprecate it in favor of the current in-progress work. This doesn't seem worth the thrash to us for a minor syntactic improvement. The ecosystem will then have to support both manual refs AND the new syntactic feature while we migrate to the newer system. Instead of just the manual refs that is already a capable option.

staltz

comment created time in a month

pull request commentfacebook/react

[fail] move IsThisRendererActing to own namespace

The issue I filed wasn't very clear but it refers to both IsSomeRendererActing and IsThisRendererActing.

I believe IsSomeRendererActing can also be made DEV only since it's only ever read in a DEV branch.

https://github.com/facebook/react/blob/a457e02ae3a2d3903fcf8748380b1cc293a2445e/packages/react/src/ReactSharedInternals.js#L19

https://github.com/facebook/react/blob/e706721490e50d0bd6af2cd933dbf857fd8b61ed/packages/react-reconciler/src/ReactFiberWorkLoop.js#L2717

threepointone

comment created time in a month

Pull request review commentfacebook/react

[fail] move IsThisRendererActing to own namespace

 const [   runEventsInBatch,   /* eslint-disable no-unused-vars */   flushPassiveEffects,-  IsThisRendererActing,   /* eslint-enable no-unused-vars */ ] = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Events; +/* eslint-disable no-unused-vars */+const [+  IsThisRendererActing,

Do we need to destruct this at all if it's not used?

However, you should probably move flushPassiveEffects to the Tests namespace too, right?

threepointone

comment created time in a month

Pull request review commentfacebook/react

[fail] move IsThisRendererActing to own namespace

 const [   runEventsInBatch,   /* eslint-enable no-unused-vars */   flushPassiveEffects,-  IsThisRendererActing, ] = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Events; +const [+  IsThisRendererActing,+] = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Tests;

How about we just don't destructure nor set IsThisRendererActing in ReactTestUtilsAct for PROD?

It's only ever read by React in DEV mode so why bother writing it in PROD? Even if we do use the rest of act in prod.

threepointone

comment created time in a month

issue commentfacebook/react

Warn for string refs where owner != __self

This is something we actually should do soon as part of deprecations work. If we first get rid of these edge cases then it will allow us to write a codemod for string refs.

jimfb

comment created time in a month

pull request commentfacebook/react

Double compress using GCC

We probably shouldn't do this for FB bundles since we expect a second pass by our bundler anyway and we already compromise if that one is not smart enough. Basically, we could solve it by compiling it again in FB infra. I guess the file size there is just noisy variables.

There's still an increase in the "react" package which is interesting to look at.

sebmarkbage

comment created time in a month

issue commentfacebook/react

SuspenseList tail property not working on re-renders

What is the experience that you want?

useTransition is for when you want to wait on the previous screen until the data is loaded.

SuspenseList is when you want to incrementally show the content on the other page.

You’ll have to pick what is the primary experience.

There is a trick you can use where you set unstable_avoidThisBoundary={true} on the Suspense boundaries. In that mode they will block the transition up until the timeout. However they will also trigger the outer fallbacks for initial render. So it won’t work with SuspenseList on initial render. You have to set it to false then.

However this is a weird combination. I’d decide what you want to do. Either stay on the previous page while loading or immediately go to the next page and let it load incrementally.

Alternative remove the boundary around the header and put it around the whole subtree. That way you can useTransition to wait for the header and then incrementally load the rest of the content.

kmurgic

comment created time in a month

push eventfacebook/react

Sebastian Markbåge

commit sha 18875b240131273c8e3dd213341edc159150d80e

Remove special casing of toString values when enableTrustedTypesIntegration (#17774) * Remove toString of dangerouslySetInnerHTML As far as I can tell, the toString call was added here: https://github.com/facebook/react/commit/caae627cd557812d28d11237b34bff6c661ea8bc#diff-5574f655d491348f422bca600ff6711dR887 It was never really needed. Subsequently when we added Trusted Types, this needed to be changed to a special call but we really should just always let it pass through. * Remove special casing of toString values when enableTrustedTypesIntegration As far as I can tell, we only toString in user space because of IE8/9. We don't really support IE8/9 anymore and by the time this flag is on, we should be able to deprecate it. Unless this is also an issue in IE11. I haven't tested yet.

view details

push time in a month

PR merged facebook/react

Remove special casing of toString values when enableTrustedTypesIntegration CLA Signed React Core Team

As far as I can tell, we only toString in user space because of IE8/9. I tested in IE10 and IE11 and it doesn't seem to be an issue there.

We don't really support IE8/9 anymore and by the time this flag is on, it should be fine to stop supporting IE8/9. Arguably it's already ok to break it since we've done so with other IE8/9 things.

+17 -82

5 comments

5 changed files

sebmarkbage

pr closed time in a month

Pull request review commentfacebook/react

Rename internal fields

 import {getParentSuspenseInstance} from './ReactDOMHostConfig'; const randomKey = Math.random()   .toString(36)   .slice(2);-const internalInstanceKey = '__reactInternalInstance$' + randomKey;

We should never do these hacks in DevTools, we should use the injected APIs to do it or add a new injected API if we don't have one.

sebmarkbage

comment created time in a month

PR opened facebook/react

Reviewers
Rename internal fields

One day we'll use private fields for this but until then I'm just going to rename these every version until people get the hint.

This is actually better than if we change fields while keeping the same name. Because at least this will fail early (and often bailout) where as once found, people tend to assume that the data structure is there and as such throws errors to users.

+7 -7

0 comment

2 changed files

pr created time in a month

create barnchsebmarkbage/react

branch : renameinstancehandle

created branch time in a month

issue openedfacebook/react

flushPassiveEffects and IsThisRendererActing are on the wrong "secret" object

They're currently attached to the "Events" namespace but they don't have anything to do with events.

Once we delete Events, after ReactTestUtils and RNW are fixed, then we won't be able to clean up the array because these two are on it.

Additionally the IsSomeRendererActing field is in the production bundles. We need to make it DEV-only.

created time in a month

issue commentfacebook/react

SuspenseList tail property not working on re-renders

This is expected behavior - albeit confusing. This is because in your example, these are not "new" rows. They're updates to existing rows. If you instead provided a "key" unique to the page either on a parent or on the row to make them new, then it would work as expected.

This is because we don't want to "delete" an item already exists. This isn't obvious when all of them suspend like in this case but when only some of them re-suspend it starts getting weird.

In fact, if you add one more row at the bottom that doesn't suspend, then revealOrder="forwards" behavior also don't be preserved because they're all considered part of the updated "head".

In general, I think the right solution is just to avoid this scenario and use a key instead.

kmurgic

comment created time in a month

pull request commentreactjs/rfcs

RFC: Context selectors

One general optimization we could do is to eagerly call the render function during propagation and see if the render yields a different result and only if it does do we clone the path to it.

Another way to look at this selector is that it's just a way to scope a part of that eager render. Another way could be to have a hook that isolates only part of the rerender.

let x = expensiveFn(props);
let y = useIsolation(() => {
   let v = useContext(MyContext);
   return selector(v);
});
return <div>{x + y}</div>;

The interesting thing about this is that it could also be used together with state. Only if this context has changed or if this state has changed and that results in a new value, do we rerender.

let y = useIsolation(() => {
   let [s, ...] = useState(...);
   let v = useContext(MyContext);
   return selector(v, s);
});

Another way to implement the same effect is to just memoize everything else instead.

let x = useMemo(() => expensiveFn(props), [props]);
let [s, ...] = useState(...);
let v = useContext(MyContext);
let y = useMemo(() => selector(v, s), [v, s]);
return useMemo(() => <div>{x + y}</div>, [x, y]);

It's hard to rely on everything else being memoized today. However, ultimately I think that this is where we're going. Albeit perhaps automated (e.g. compiler driven).

If that is the case, I wonder if this API will in fact be necessary or if it's just something that we get naturally for free by memoizing more parts of a render function.

gnoff

comment created time in a month

pull request commentreactjs/rfcs

RFC: Lazy Context Propagation

Since you have an implementation now, I wonder if you have some data on how much this helps in real apps?

One thing to consider is that the reason context propagation is acceptable fast when optimized (it's actually unnecessary slow now, thanks to code rot, and we need to optimize it again), is because both code an a lot of the data is very local and benefit a lot from caches. Locality can make brute force surprisingly fast.

By doing this propagation over and over, we might negate some of those benefits. Especially when only a single context updates.

gnoff

comment created time in a month

push eventfacebook/react

Sebastian Markbåge

commit sha edeea0720791f998b505f2ecdcf866c7e539e7a2

Remove toString of dangerouslySetInnerHTML (#17773) As far as I can tell, the toString call was added here: https://github.com/facebook/react/commit/caae627cd557812d28d11237b34bff6c661ea8bc#diff-5574f655d491348f422bca600ff6711dR887 It was never really needed. Subsequently when we added Trusted Types, this needed to be changed to a special call but we really should just always let it pass through.

view details

push time in a month

PR merged facebook/react

Reviewers
Remove toString of dangerouslySetInnerHTML CLA Signed

As far as I can tell, the toString call was added here:

https://github.com/facebook/react/commit/caae627cd557812d28d11237b34bff6c661ea8bc#diff-5574f655d491348f422bca600ff6711dR887

However, we don't toString the HTML in the initial creation. Only updates. It was never really needed.

Subsequently when we added Trusted Types, this needed to be changed to a special call but we really should just always let it pass through.

Interestingly we should probably always let values pass through except for when we need to do something special with the string which is rare.

+1 -5

3 comments

1 changed file

sebmarkbage

pr closed time in a month

pull request commentfacebook/react

Add useOpaqueReference Hook

If we land https://github.com/facebook/react/pull/17774 then this will need to be rebased on it.

It removes any brand checks in React. This makes it a bit awkward to add just for this case.

An alternative approach could be that toString/valueOf calls setId when accessed and returning empty string. That way it happens automatically by the DOM calling these methods. Then we can add a DEV error if these are called in a different context than React calling them. E.g. we can detect this object in DEV only and do the special case but then that way there's no special brand checking in prod.

lunaruan

comment created time in a month

Pull request review commentfacebook/react

Add useOpaqueReference Hook

 function updateTransition(   return [start, isPending]; } +let clientId: number = 0;++function mountOpaqueReference(): string | IdObject {+  const [id, setId] = mountState(() => {+    if (getIsHydrating()) {+      return {+        $$typeof: REACT_OPAQUE_OBJECT_TYPE,+        _setId: () => {

It is possible for this function to be called more than once if the object is referenced multiple times. This would eventually get rendered but unnecessarily queue up many updates and waste IDs that won't get used. We might want to dedupe so that if it's called more than once, we don't generate a new id each time.

lunaruan

comment created time in a month

push eventsebmarkbage/react

Sebastian Markbage

commit sha ac96bc5748ad18e53b9234f2b05b680fb072d9a7

Remove special casing of toString values when enableTrustedTypesIntegration As far as I can tell, we only toString in user space because of IE8/9. We don't really support IE8/9 anymore and by the time this flag is on, we should be able to deprecate it. Unless this is also an issue in IE11. I haven't tested yet.

view details

push time in a month

pull request commentfacebook/react

Remove special casing of toString values when enableTrustedTypesIntegration

I also removed the fancy typed abstractions. They actually provide more harm than they help. It makes it seem like you should go through them to get to a string or string like object but often you should just special case it at every callsite. Like this one: https://github.com/facebook/react/pull/17322/files#r362947154

The abstractions makes it seem like it can be used in other places but it's really only useful in one place and so we should inline it in that one place.

sebmarkbage

comment created time in a month

push eventsebmarkbage/react

Sebastian Markbage

commit sha 09eedd364c58154fc649e60e555e8f3838e434e8

Remove special casing of toString values when enableTrustedTypesIntegration As far as I can tell, we only toString in user space because of IE8/9. We don't really support IE8/9 anymore and by the time this flag is on, we should be able to deprecate it. Unless this is also an issue in IE11. I haven't tested yet.

view details

push time in a month

PR opened facebook/react

Reviewers
Remove special casing of toString values when enableTrustedTypesIntegration

As far as I can tell, we only toString in user space because of IE8/9.

We don't really support IE8/9 anymore and by the time this flag is on, we should be able to deprecate it.

Unless this is also an issue in IE11. I haven't tested yet.

+13 -36

0 comment

3 changed files

pr created time in a month

create barnchsebmarkbage/react

branch : rmtostring

created branch time in a month

push eventsebmarkbage/react

Sebastian Markbage

commit sha 9b1d1839b3d749cf4122f6cff970b2f5cbc0401a

Remove toString of dangerouslySetInnerHTML As far as I can tell, the toString call was added here: https://github.com/facebook/react/commit/caae627cd557812d28d11237b34bff6c661ea8bc#diff-5574f655d491348f422bca600ff6711dR887 It was never really needed. Subsequently when we added Trusted Types, this needed to be changed to a special call but we really should just always let it pass through.

view details

push time in a month

PR opened facebook/react

Reviewers
Remove toString of dangerouslySetInnerHTML

As far as I can tell, the toString call was added here:

https://github.com/facebook/react/commit/caae627cd557812d28d11237b34bff6c661ea8bc#diff-5574f655d491348f422bca600ff6711dR887

However, we don't toString the HTML in the initial creation. Only updates. It was never really needed.

Subsequently when we added Trusted Types, this needed to be changed to a special call but we really should just always let it pass through.

Interestingly we should probably always let values pass through except for when we need to do something special with the string which is rare.

+1 -2

0 comment

1 changed file

pr created time in a month

create barnchsebmarkbage/react

branch : rmhtmltostring

created branch time in a month

Pull request review commentfacebook/react

React.unstable_avoidThisRender()

 if (enableJSXTransformAPI) {   } } +if (enableAvoidThisRenderAPI) {+  React.unstable_avoidThisRender = unstable_avoidThisRender;

This React.unstable_avoidThisRender is the only place you need the unstable_ prefix.

threepointone

comment created time in a month

Pull request review commentfacebook/react

React.unstable_avoidThisRender()

 function completeWork(       if ((workInProgress.effectTag & DidCapture) !== NoEffect) {         // Something suspended. Re-render with the fallback children.         workInProgress.expirationTime = renderExpirationTime;+         // Do not reset the effect list.         return workInProgress;+      } else if ((workInProgress.effectTag & SoftCapture) !== NoEffect) {+        // something called avoidThisRender()+        if (+          current === null ||+          (current.memoizedState &&+            renderExpirationTime > current.memoizedState.didAvoidRenderTime)+        ) {+          // Something soft suspended, and this is the first "trigger rerender" pass.+          // Re-render with the fallback children.+          if (!workInProgress.memoizedState) {+            // fallback isn't showing yet, trigger fallback pass+            workInProgress.expirationTime = renderExpirationTime;+            return workInProgress;+          } else {+            // TODO: we're never entering this branch because+            // effectTag: SoftCapture is cleared after the previous pass+            // does it matter?

You could make it an invariant that workInProgress.memoizedState is null. If it's not, then there's a bug somewhere since any throwing/avoidThisRender in the fallback should bubble past this. That way you also don't have to add a fake return null.

threepointone

comment created time in a month

Pull request review commentfacebook/react

React.unstable_avoidThisRender()

 export type Dispatcher = {   useTransition(     config: SuspenseConfig | void | null,   ): [(() => void) => void, boolean],++  unstable_avoidThisRender: typeof unstable_avoidThisRender,

Let's write out the expected signature explicitly. We also don't need to make this name "unstable" prefixed since it's a private API. It just adds bytes.

threepointone

comment created time in a month

Pull request review commentfacebook/react

React.unstable_avoidThisRender()

 function updateSuspenseComponent(       primaryChildFragment.sibling = fallbackChildFragment;       // Skip the primary children, and continue working on the       // fallback children.-      workInProgress.memoizedState = SUSPENDED_MARKER;++      if (workInProgress.effectTag & SoftCapture) {+        // don't clear the effectTag yet, it'll be read in completeWork+        workInProgress.effectTag &= ~SoftCapture;+        const avoidedRenderExpiration = renderExpirationTime - 1; // verify/rename+        // this means there's work to be done on this fiber "in the future"+        workInProgress.memoizedState = {+          dehydrated: null,+          retryTime: NoWork,+          didAvoidRenderTime: avoidedRenderExpiration,+        };++        workInProgress.expirationTime = avoidedRenderExpiration;+      } else {+        // workInProgress.effectTag & DidCapture is truthy

This comment is a bit too specific. It's just describing the technical details (also, we shouldn't actually be comparing truthiness but compare against zero so that's an oversight). You might want to describe it more as a higher level concept.

However, I think this points out a bug though. What if both are set? In completeWork, DidCapture wins, but in beginWork SoftCapture wins. That seems like it could cause havoc.

threepointone

comment created time in a month

more