Skip to content

Commit

Permalink
fix: update implementation of getReactFiberFromNode to be compatible …
Browse files Browse the repository at this point in the history
…with React 17 (#16392)

* fix: update implementaion of getReactFiberFromNode to be compatible with React 17

* add changelog entry

* add a UT
  • Loading branch information
layershifter committed Jan 6, 2021
1 parent 74ae5d2 commit 856a4e5
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 276 deletions.
1 change: 1 addition & 0 deletions packages/fluentui/CHANGELOG.md
Expand Up @@ -78,6 +78,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Fix throwing error in `fontSizeUtility` on CodeSandbox @layershifter ([#16368](https://github.com/microsoft/fluentui/pull/16368))
- Fix - Apply aria attributes on proper element for `Datepicker`, `Dropdown`, `Input` @kolaps33 ([#16324](https://github.com/microsoft/fluentui/pull/16324))
- Fix a warning when the `fluid` or `resize` prop was used in `TextArea` @assuncaocharles ([#16393](https://github.com/microsoft/fluentui/pull/16393))
- Fix implementation of getReactFiberFromNode to be compatible with React 17 @layershifter ([#16392](https://github.com/microsoft/fluentui/pull/16392))

### Features
- Add 2.0 light and dark themes @jurokapsiar ([#15867](https://github.com/microsoft/fluentui/pull/15867))
Expand Down
@@ -1,263 +1,11 @@
import * as React from 'react';
import { ForwardRef } from 'react-is';

// ========================================================
// react/packages/shared/ReactTypes.js
// ========================================================

type ReactEventResponder<E, C> = {
$$typeof: Symbol | number;
displayName: string;
targetEventTypes: null | string[];
rootEventTypes: null | string[];
getInitialState: null | ((props: Object) => Object);
onEvent: null | ((event: E, context: C, props: Object, state: Object) => void);
onRootEvent: null | ((event: E, context: C, props: Object, state: Object) => void);
onMount: null | ((context: C, props: Object, state: Object) => void);
onUnmount: null | ((context: C, props: Object, state: Object) => void);
};

type ReactEventResponderInstance<E, C> = {
fiber: Object;
props: Object;
responder: ReactEventResponder<E, C>;
rootEventTypes: null | Set<string>;
state: Object;
};

// ========================================================
// react/packages/react-reconciler/src/ReactFiberHooks.js
// ========================================================

export type HookType =
| 'useState'
| 'useReducer'
| 'useContext'
| 'useRef'
| 'useEffect'
| 'useLayoutEffect'
| 'useCallback'
| 'useMemo'
| 'useImperativeHandle'
| 'useDebugValue'
| 'useResponder';

type ReactProviderType<T> = {
$$typeof: Symbol | number;
_context: ReactContext<T>;
};

type ReactContext<T> = {
$$typeof: Symbol | number;
Consumer: ReactContext<T>;
Provider: ReactProviderType<T>;

_calculateChangedBits: ((a: T, b: T) => number) | null;

_currentValue: T;
_currentValue2: T;
_threadCount: number;

// DEV only
_currentRenderer?: Object | null;
_currentRenderer2?: Object | null;
};

type ContextDependency<T> = {
context: ReactContext<T>;
observedBits: number;
next: ContextDependency<any> | null;
};

enum WorkTag {
FunctionComponent = 0,
ClassComponent = 1,
IndeterminateComponent = 2, // Before we know whether it is function or class
HostRoot = 3, // Root of a host tree. Could be nested inside another node.
HostPortal = 4, // A subtree. Could be an entry point to a different renderer.
HostComponent = 5,
HostText = 6,
Fragment = 7,
Mode = 8,
ContextConsumer = 9,
ContextProvider = 10,
ForwardRef = 11,
Profiler = 12,
SuspenseComponent = 13,
MemoComponent = 14,
SimpleMemoComponent = 15,
LazyComponent = 16,
IncompleteClassComponent = 17,
DehydratedFragment = 18,
SuspenseListComponent = 19,
FundamentalComponent = 20,
ScopeComponent = 21,
}

type Source = {
fileName: string;
lineNumber: number;
};

type ExpirationTime = number;

type Dependencies = {
expirationTime: ExpirationTime;
firstContext: ContextDependency<any> | null;
responders: Map<ReactEventResponder<any, any>, ReactEventResponderInstance<any, any>> | null;
};

// ========================================================
// react/packages/react-reconciler/src/ReactFiber.js
// ========================================================

// A Fiber is work on a Component that needs to be done or was done. There can
// be more than one per component.
type Fiber = {
// These first fields are conceptually members of an Instance. This used to
// be split into a separate type and intersected with the other Fiber fields,
// but until Flow fixes its intersection bugs, we've merged them into a
// single type.

// An Instance is shared between all versions of a component. We can easily
// break this out into a separate object to avoid copying so much to the
// alternate versions of the tree. We put this on a single object for now to
// minimize the number of objects created during the initial render.

// Tag identifying the type of fiber.
tag: WorkTag;

// Unique identifier of this child.
key: null | string;

// The value of element.type which is used to preserve the identity during
// reconciliation of this child.
elementType: any;

// The resolved function/class/ associated with this fiber.
type: any;

// The local state associated with this fiber.
stateNode: any;

// Conceptual aliases
// parent : Instance -> return The parent happens to be the same as the
// return fiber since we've merged the fiber and instance.

// Remaining fields belong to Fiber

// The Fiber to return to after finishing processing this one.
// This is effectively the parent, but there can be multiple parents (two)
// so this is only the parent of the thing we're currently processing.
// It is conceptually the same as the return address of a stack frame.
return: Fiber | null;

// Singly Linked List Tree Structure.
child: Fiber | null;
sibling: Fiber | null;
index: number;

// The ref last used to attach this node.
// I'll avoid adding an owner field for prod and model that as functions.
ref: React.Ref<any>;

// Input is the data coming into process this fiber. Arguments. Props.
pendingProps: any; // This type will be more specific once we overload the tag.
memoizedProps: any; // The props used to create the output.

// A queue of state updates and callbacks.
// updateQueue: UpdateQueue<any> | null,

// The state used to create the output
memoizedState: any;

// Dependencies (contexts, events) for this fiber, if it has any
dependencies: Dependencies | null;

// // Bitfield that describes properties about the fiber and its subtree. E.g.
// // the ConcurrentMode flag indicates whether the subtree should be async-by-
// // default. When a fiber is created, it inherits the mode of its
// // parent. Additional flags can be set at creation time, but after that the
// // value should remain unchanged throughout the fiber's lifetime, particularly
// // before its child fibers are created.
// mode: TypeOfMode
//
// // Effect
// effectTag: SideEffectTag

// Singly linked list fast path to the next fiber with side-effects.
nextEffect: Fiber | null;

// The first and last fiber with side-effect within this subtree. This allows
// us to reuse a slice of the linked list when we reuse the work done within
// this fiber.
firstEffect: Fiber | null;
lastEffect: Fiber | null;

// Represents a time in the future by which this work should be completed.
// Does not include work found in its subtree.
expirationTime: ExpirationTime;

// This is used to quickly determine if a subtree has no pending changes.
childExpirationTime: ExpirationTime;

// This is a pooled version of a Fiber. Every fiber that gets updated will
// eventually have a pair. There are cases when we can clean up pairs to save
// memory if we need to.
alternate: Fiber | null;

// Time spent rendering this Fiber and its descendants for the current update.
// This tells us how well the tree makes use of sCU for memoization.
// It is reset to 0 each time we render and only updated when we don't bailout.
// This field is only set when the enableProfilerTimer flag is enabled.
actualDuration?: number;

// If the Fiber is currently active in the "render" phase,
// This marks the time at which the work began.
// This field is only set when the enableProfilerTimer flag is enabled.
actualStartTime?: number;

// Duration of the most recent render time for this Fiber.
// This value is not updated when we bailout for memoization purposes.
// This field is only set when the enableProfilerTimer flag is enabled.
selfBaseDuration?: number;

// Sum of base times for all descendants of this Fiber.
// This value bubbles up during the "complete" phase.
// This field is only set when the enableProfilerTimer flag is enabled.
treeBaseDuration?: number;

// Conceptual aliases
// workInProgress : Fiber -> alternate The alternate used for reuse happens
// to be the same as work in progress.
// __DEV__ only
_debugID?: number;
_debugSource?: Source | null;
_debugOwner?: Fiber | null;
_debugIsCurrentlyTiming?: boolean;
_debugNeedsRemount?: boolean;

// Used to verify that the order of hooks does not change between renders.
_debugHookTypes?: HookType[] | null;
};
import { getReactFiberFromNode, Fiber } from '../../utils/getReactFiberFromNode';

const isDOMNode = e => e && typeof e.tagName === 'string' && e.nodeType === Node.ELEMENT_NODE;

export class FiberNavigator {
__fiber: Fiber;

static domNodeToReactFiber = (elm: HTMLElement): Fiber => {
if (!elm) return null;

for (const k in elm) {
if (k.startsWith('__reactInternalInstance$')) {
return elm[k];
}
}

return null;
};

// TODO: Fibers can become stale.
// The only current fiber is the one found on the DOM node.
// There is no way to start at a React Component fiber, go the DOM node,
Expand All @@ -279,7 +27,7 @@ export class FiberNavigator {
};

static fromDOMNode = domNode => {
const fiber = FiberNavigator.domNodeToReactFiber(domNode);
const fiber = getReactFiberFromNode(domNode);

if (!fiber) return null;

Expand Down

0 comments on commit 856a4e5

Please sign in to comment.