Allow returning null as host context (#9278)
If your renderer doesn't use host context, you might prefer to return null. This used to give an error: > Invariant Violation: Expected host context to exist. This error is likely caused by a bug in React. Please file an issue. I use a sentinel value instead now. The code in ReactFiberHostContext is a little complicated now. We could probably also just remove the invariants.
Ben Alpert committed
Mar 30, 2017 at 13:18 UTC
e57ad7f51ebca844d3e86f43b39989fe95f4b6dd
3 files changed
+90
-29
scripts/fiber/tests-passing.txt
+3
@@ -1596,6 +1596,9 @@ src/renderers/shared/fiber/__tests__/ReactCoroutine-test.js
1596
* should unmount a composite in a coroutine
1597
* should handle deep updates in coroutine
1598
1599
+src/renderers/shared/fiber/__tests__/ReactFiberHostContext-test.js
1600
+* works with null host context
1601
+
1602
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
1603
* should render a simple component
1604
* should render a simple component, in steps if needed
src/renderers/shared/fiber/ReactFiberHostContext.js
+25
-29
@@ -16,8 +16,6 @@ import type {Fiber} from 'ReactFiber';
16
import type {HostConfig} from 'ReactFiberReconciler';
17
import type {StackCursor} from 'ReactFiberStack';
18
19
-const emptyObject = require('fbjs/lib/emptyObject');
20
-
19
const {
20
createCursor,
21
pop,
@@ -26,6 +24,9 @@ const {
24
25
const invariant = require('fbjs/lib/invariant');
26
27
+declare class NoContextT {}
28
+const NO_CONTEXT: NoContextT = ({}: any);
29
+
30
export type HostContext<C, CX> = {
31
getHostContext(): CX,
32
getRootHostContainer(): C,
@@ -44,19 +45,27 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
45
getRootHostContext,
46
} = config;
47
47
- let contextStackCursor: StackCursor<CX | null> = createCursor((null: ?CX));
48
- let contextFiberStackCursor: StackCursor<Fiber | null> = createCursor(
49
- (null: Fiber | null),
48
+ let contextStackCursor: StackCursor<CX | NoContextT> = createCursor(
49
+ NO_CONTEXT,
50
+ );
51
+ let contextFiberStackCursor: StackCursor<Fiber | NoContextT> = createCursor(
52
+ NO_CONTEXT,
53
+ );
54
+ let rootInstanceStackCursor: StackCursor<C | NoContextT> = createCursor(
55
+ NO_CONTEXT,
56
);
51
- let rootInstanceStackCursor: StackCursor<C | null> = createCursor((null: ?C));
57
53
- function getRootHostContainer(): C {
54
- const rootInstance = rootInstanceStackCursor.current;
58
+ function requiredContext<Value>(c: Value | NoContextT): Value {
59
invariant(
56
- rootInstance !== null,
57
- 'Expected root container to exist. This error is likely caused by a ' +
58
- 'bug in React. Please file an issue.',
60
+ c !== NO_CONTEXT,
61
+ 'Expected host context to exist. This error is likely caused by a bug ' +
62
+ 'in React. Please file an issue.',
63
);
64
+ return (c: any);
65
+ }
66
+
67
+ function getRootHostContainer(): C {
68
+ const rootInstance = requiredContext(rootInstanceStackCursor.current);
69
return rootInstance;
70
}
71
@@ -80,26 +89,13 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
89
}
90
91
function getHostContext(): CX {
83
- const context = contextStackCursor.current;
84
- invariant(
85
- context != null,
86
- 'Expected host context to exist. This error is likely caused by a bug ' +
87
- 'in React. Please file an issue.',
88
- );
92
+ const context = requiredContext(contextStackCursor.current);
93
return context;
94
}
95
96
function pushHostContext(fiber: Fiber): void {
93
- const rootInstance = rootInstanceStackCursor.current;
94
- invariant(
95
- rootInstance != null,
96
- 'Expected root host context to exist. This error is likely caused by ' +
97
- 'a bug in React. Please file an issue.',
98
- );
99
-
100
- const context = contextStackCursor.current !== null
101
- ? contextStackCursor.current
102
- : emptyObject;
97
+ const rootInstance = requiredContext(rootInstanceStackCursor.current);
98
+ const context = requiredContext(contextStackCursor.current);
99
const nextContext = getChildHostContext(context, fiber.type, rootInstance);
100
101
// Don't push this Fiber's context unless it's unique.
@@ -125,8 +121,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
121
}
122
123
function resetHostContainer() {
128
- contextStackCursor.current = null;
129
- rootInstanceStackCursor.current = null;
124
+ contextStackCursor.current = NO_CONTEXT;
125
+ rootInstanceStackCursor.current = NO_CONTEXT;
126
}
127
128
return {
src/renderers/shared/fiber/__tests__/ReactFiberHostContext-test.js
new
+62
@@ -0,0 +1,62 @@
1
+/**
2
+ * Copyright 2013-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @emails react-core
10
+ */
11
+
12
+'use strict';
13
+
14
+var React;
15
+var ReactFiberReconciler;
16
+
17
+describe('ReactFiberHostContext', () => {
18
+ beforeEach(() => {
19
+ jest.resetModules();
20
+ React = require('React');
21
+ ReactFiberReconciler = require('ReactFiberReconciler');
22
+ });
23
+
24
+ it('works with null host context', () => {
25
+ var creates = 0;
26
+ var Renderer = ReactFiberReconciler({
27
+ prepareForCommit: function() {},
28
+ resetAfterCommit: function() {},
29
+ getRootHostContext: function() {
30
+ return null;
31
+ },
32
+ getChildHostContext: function() {
33
+ return null;
34
+ },
35
+ shouldSetTextContent: function() {
36
+ return false;
37
+ },
38
+ createInstance: function() {
39
+ creates++;
40
+ },
41
+ finalizeInitialChildren: function() {
42
+ return null;
43
+ },
44
+ appendInitialChild: function() {
45
+ return null;
46
+ },
47
+ appendChild: function() {
48
+ return null;
49
+ },
50
+ useSyncScheduling: true,
51
+ });
52
+
53
+ const container = Renderer.createContainer(/* root: */ null);
54
+ Renderer.updateContainer(
55
+ <a><b /></a>,
56
+ container,
57
+ /* parentComponent: */ null,
58
+ /* callback: */ null,
59
+ );
60
+ expect(creates).toBe(2);
61
+ });
62
+});