@samitouri / QOS-React / commits / 8e69bc45aa

Make host context use null as empty and only error in dev (#25609)

Makes it slightly more blazing. No host config actually uses null as any useful and we use this as a placeholder value anyway. It's also better for perf since it doesn't let two different hidden classes pass around. It's also a magic place holder that triggers error if we do try to access anything from it.

Sebastian Markbåge committed Nov 1, 2022 at 19:02 UTC 8e69bc45aa2edf9fba0f5a26f1db6a4a1f87d70f
3 files changed +33 -109
packages/react-reconciler/src/ReactFiberHostContext.new.js
+15 -22
@@ -14,33 +14,26 @@ import type {Container, HostContext} from './ReactFiberHostConfig';
14 import {getChildHostContext, getRootHostContext} from './ReactFiberHostConfig';
15 import {createCursor, push, pop} from './ReactFiberStack.new';
16
17 -declare class NoContextT {}
18 -const NO_CONTEXT: NoContextT = ({}: any);
19 -
20 -const contextStackCursor: StackCursor<HostContext | NoContextT> = createCursor(
21 - NO_CONTEXT,
22 -);
23 -const contextFiberStackCursor: StackCursor<Fiber | NoContextT> = createCursor(
24 - NO_CONTEXT,
17 +const contextStackCursor: StackCursor<HostContext | null> = createCursor(null);
18 +const contextFiberStackCursor: StackCursor<Fiber | null> = createCursor(null);
19 +const rootInstanceStackCursor: StackCursor<Container | null> = createCursor(
20 + null,
21 );
26 -const rootInstanceStackCursor: StackCursor<
27 - Container | NoContextT,
28 -> = createCursor(NO_CONTEXT);
29 -
30 -function requiredContext<Value>(c: Value | NoContextT): Value {
31 - if (c === NO_CONTEXT) {
32 - throw new Error(
33 - 'Expected host context to exist. This error is likely caused by a bug ' +
34 - 'in React. Please file an issue.',
35 - );
36 - }
22
23 +function requiredContext<Value>(c: Value | null): Value {
24 + if (__DEV__) {
25 + if (c === null) {
26 + console.error(
27 + 'Expected host context to exist. This error is likely caused by a bug ' +
28 + 'in React. Please file an issue.',
29 + );
30 + }
31 + }
32 return (c: any);
33 }
34
35 function getCurrentRootHostContainer(): null | Container {
42 - const container = rootInstanceStackCursor.current;
43 - return container === NO_CONTEXT ? null : ((container: any): Container);
36 + return rootInstanceStackCursor.current;
37 }
38
39 function getRootHostContainer(): Container {
@@ -61,7 +54,7 @@ function pushHostContainer(fiber: Fiber, nextRootInstance: Container) {
54 // we'd have a different number of entries on the stack depending on
55 // whether getRootHostContext() throws somewhere in renderer code or not.
56 // So we push an empty value first. This lets us safely unwind on errors.
64 - push(contextStackCursor, NO_CONTEXT, fiber);
57 + push(contextStackCursor, null, fiber);
58 const nextRootContext = getRootHostContext(nextRootInstance);
59 // Now that we know this function doesn't throw, replace it.
60 pop(contextStackCursor, fiber);
packages/react-reconciler/src/ReactFiberHostContext.old.js
+15 -22
@@ -14,33 +14,26 @@ import type {Container, HostContext} from './ReactFiberHostConfig';
14 import {getChildHostContext, getRootHostContext} from './ReactFiberHostConfig';
15 import {createCursor, push, pop} from './ReactFiberStack.old';
16
17 -declare class NoContextT {}
18 -const NO_CONTEXT: NoContextT = ({}: any);
19 -
20 -const contextStackCursor: StackCursor<HostContext | NoContextT> = createCursor(
21 - NO_CONTEXT,
22 -);
23 -const contextFiberStackCursor: StackCursor<Fiber | NoContextT> = createCursor(
24 - NO_CONTEXT,
17 +const contextStackCursor: StackCursor<HostContext | null> = createCursor(null);
18 +const contextFiberStackCursor: StackCursor<Fiber | null> = createCursor(null);
19 +const rootInstanceStackCursor: StackCursor<Container | null> = createCursor(
20 + null,
21 );
26 -const rootInstanceStackCursor: StackCursor<
27 - Container | NoContextT,
28 -> = createCursor(NO_CONTEXT);
29 -
30 -function requiredContext<Value>(c: Value | NoContextT): Value {
31 - if (c === NO_CONTEXT) {
32 - throw new Error(
33 - 'Expected host context to exist. This error is likely caused by a bug ' +
34 - 'in React. Please file an issue.',
35 - );
36 - }
22
23 +function requiredContext<Value>(c: Value | null): Value {
24 + if (__DEV__) {
25 + if (c === null) {
26 + console.error(
27 + 'Expected host context to exist. This error is likely caused by a bug ' +
28 + 'in React. Please file an issue.',
29 + );
30 + }
31 + }
32 return (c: any);
33 }
34
35 function getCurrentRootHostContainer(): null | Container {
42 - const container = rootInstanceStackCursor.current;
43 - return container === NO_CONTEXT ? null : ((container: any): Container);
36 + return rootInstanceStackCursor.current;
37 }
38
39 function getRootHostContainer(): Container {
@@ -61,7 +54,7 @@ function pushHostContainer(fiber: Fiber, nextRootInstance: Container) {
54 // we'd have a different number of entries on the stack depending on
55 // whether getRootHostContext() throws somewhere in renderer code or not.
56 // So we push an empty value first. This lets us safely unwind on errors.
64 - push(contextStackCursor, NO_CONTEXT, fiber);
57 + push(contextStackCursor, null, fiber);
58 const nextRootContext = getRootHostContext(nextRootInstance);
59 // Now that we know this function doesn't throw, replace it.
60 pop(contextStackCursor, fiber);
packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js
+3 -65
@@ -30,72 +30,10 @@ describe('ReactFiberHostContext', () => {
30
31 global.IS_REACT_ACT_ENVIRONMENT = true;
32
33 - // @gate __DEV__
34 - it('works with null host context', async () => {
35 - let creates = 0;
36 - const Renderer = ReactFiberReconciler({
37 - prepareForCommit: function() {
38 - return null;
39 - },
40 - resetAfterCommit: function() {},
41 - getRootHostContext: function() {
42 - return null;
43 - },
44 - getChildHostContext: function() {
45 - return null;
46 - },
47 - shouldSetTextContent: function() {
48 - return false;
49 - },
50 - createInstance: function() {
51 - creates++;
52 - },
53 - finalizeInitialChildren: function() {
54 - return null;
55 - },
56 - appendInitialChild: function() {
57 - return null;
58 - },
59 - now: function() {
60 - return 0;
61 - },
62 - appendChildToContainer: function() {
63 - return null;
64 - },
65 - clearContainer: function() {},
66 - getCurrentEventPriority: function() {
67 - return DefaultEventPriority;
68 - },
69 - prepareRendererToRender: function() {},
70 - resetRendererAfterRender: function() {},
71 - supportsMutation: true,
72 - requestPostPaintCallback: function() {},
73 - });
74 -
75 - const container = Renderer.createContainer(
76 - /* root: */ null,
77 - ConcurrentRoot,
78 - null,
79 - false,
80 - '',
81 - null,
82 - );
83 - act(() => {
84 - Renderer.updateContainer(
85 - <a>
86 - <b />
87 - </a>,
88 - container,
89 - /* parentComponent: */ null,
90 - /* callback: */ null,
91 - );
92 - });
93 - expect(creates).toBe(2);
94 - });
95 -
33 // @gate __DEV__
34 it('should send the context to prepareForCommit and resetAfterCommit', () => {
35 const rootContext = {};
36 + const childContext = {};
37 const Renderer = ReactFiberReconciler({
38 prepareForCommit: function(hostContext) {
39 expect(hostContext).toBe(rootContext);
@@ -105,10 +43,10 @@ describe('ReactFiberHostContext', () => {
43 expect(hostContext).toBe(rootContext);
44 },
45 getRootHostContext: function() {
108 - return null;
46 + return rootContext;
47 },
48 getChildHostContext: function() {
111 - return null;
49 + return childContext;
50 },
51 shouldSetTextContent: function() {
52 return false;