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;