Skip to content

Commit 197b461

Browse files
committed
Don't allow recursive sync updates during reconciliation (begin phase)
We can't allow this because it may lead to infinite loops. Recursion is only safe during the commit phase. Prints a warning and downgrades to Task priority.
1 parent 62fc5b7 commit 197b461

13 files changed

+78
-56
lines changed

scripts/fiber/tests-failing.txt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ src/renderers/shared/__tests__/ReactPerf-test.js
5656
* should include render time of functional components
5757
* should not count time in a portal towards lifecycle method
5858
* should work when measurement starts during reconciliation
59-
* should not print errant warnings if portal throws in render()
60-
* should not print errant warnings if portal throws in componentWillMount()
6159

6260
src/renderers/shared/hooks/__tests__/ReactComponentTreeHook-test.js
6361
* can be retrieved by ID
@@ -72,7 +70,6 @@ src/renderers/shared/shared/__tests__/ReactComponentLifeCycle-test.js
7270
* should carry through each of the phases of setup
7371

7472
src/renderers/shared/shared/__tests__/ReactCompositeComponent-test.js
75-
* should disallow nested render calls
7673
* should update refs if shouldComponentUpdate gives false
7774

7875
src/renderers/shared/shared/__tests__/ReactEmptyComponent-test.js
@@ -95,5 +92,3 @@ src/renderers/shared/shared/__tests__/ReactUpdates-test.js
9592

9693
src/renderers/shared/shared/__tests__/refs-test.js
9794
* Should increase refs with an increase in divs
98-
* attaches, detaches from fiber component with stack layer
99-
* attaches, detaches from stack component with fiber layer

scripts/fiber/tests-passing-except-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ src/renderers/shared/shared/__tests__/ReactCompositeComponent-test.js
112112
* should warn about `setState` on unmounted components
113113
* should warn about `setState` in render
114114
* should warn about `setState` in getChildContext
115+
* should disallow nested render calls
115116

116117
src/renderers/shared/shared/__tests__/ReactMultiChild-test.js
117118
* should warn for using maps as children with owner info

scripts/fiber/tests-passing.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,8 @@ src/renderers/shared/__tests__/ReactPerf-test.js
11171117
* should not print errant warnings if render() throws
11181118
* should not print errant warnings if componentWillMount() throws
11191119
* should not print errant warnings if componentDidMount() throws
1120+
* should not print errant warnings if portal throws in render()
1121+
* should not print errant warnings if portal throws in componentWillMount()
11201122
* should not print errant warnings if portal throws in componentDidMount()
11211123

11221124
src/renderers/shared/fiber/__tests__/ReactCoroutine-test.js
@@ -1581,6 +1583,8 @@ src/renderers/shared/shared/__tests__/refs-test.js
15811583
* ref called correctly for stateless component when __DEV__ = false
15821584
* ref called correctly for stateless component when __DEV__ = true
15831585
* coerces numbers to strings
1586+
* attaches, detaches from fiber component with stack layer
1587+
* attaches, detaches from stack component with fiber layer
15841588

15851589
src/renderers/shared/shared/event/__tests__/EventPluginHub-test.js
15861590
* should prevent non-function listeners, at dispatch

src/renderers/dom/fiber/ReactDOMFiber.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ type HostContextDev = {
6767
};
6868
type HostContextProd = string;
6969
type HostContext = HostContextDev | HostContextProd;
70-
71-
let eventsEnabled : ?boolean = null;
72-
let selectionInformation : ?mixed = null;
70+
type CommitInfo = {
71+
eventsEnabled: boolean,
72+
selectionInformation: mixed,
73+
};
7374

7475
var ELEMENT_NODE_TYPE = 1;
7576
var DOC_NODE_TYPE = 9;
@@ -123,17 +124,18 @@ var DOMRenderer = ReactFiberReconciler({
123124
return getChildNamespace(parentNamespace, type);
124125
},
125126

126-
prepareForCommit() : void {
127-
eventsEnabled = ReactBrowserEventEmitter.isEnabled();
127+
prepareForCommit() : CommitInfo {
128+
const eventsEnabled = ReactBrowserEventEmitter.isEnabled();
128129
ReactBrowserEventEmitter.setEnabled(false);
129-
selectionInformation = ReactInputSelection.getSelectionInformation();
130+
return {
131+
eventsEnabled,
132+
selectionInformation: ReactInputSelection.getSelectionInformation(),
133+
};
130134
},
131135

132-
resetAfterCommit() : void {
133-
ReactInputSelection.restoreSelection(selectionInformation);
134-
selectionInformation = null;
135-
ReactBrowserEventEmitter.setEnabled(eventsEnabled);
136-
eventsEnabled = null;
136+
resetAfterCommit(commitInfo : CommitInfo) : void {
137+
ReactInputSelection.restoreSelection(commitInfo.selectionInformation);
138+
ReactBrowserEventEmitter.setEnabled(commitInfo.eventsEnabled);
137139
},
138140

139141
createInstance(

src/renderers/shared/__tests__/ReactPerf-test.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ describe('ReactPerf', () => {
614614
}
615615
}
616616
class EvilPortal extends React.Component {
617-
componentWillMount() {
617+
componentDidMount() {
618618
var portalContainer = document.createElement('div');
619619
ReactDOM.render(<Evil />, portalContainer);
620620
}
@@ -644,6 +644,7 @@ describe('ReactPerf', () => {
644644
it('should not print errant warnings if portal throws in componentWillMount()', () => {
645645
var container = document.createElement('div');
646646
var thrownErr = new Error('Muhaha!');
647+
spyOn(console, 'error');
647648

648649
class Evil extends React.Component {
649650
componentWillMount() {
@@ -679,6 +680,13 @@ describe('ReactPerf', () => {
679680
}
680681
ReactDOM.unmountComponentAtNode(container);
681682
ReactPerf.stop();
683+
684+
// A sync `render` inside cWM will print a warning. That should be the
685+
// only warning.
686+
expect(console.error.calls.count()).toEqual(1);
687+
expect(console.error.calls.argsFor(0)[0]).toMatch(
688+
/Render methods should be a pure function of props and state/
689+
);
682690
});
683691

684692
it('should not print errant warnings if portal throws in componentDidMount()', () => {
@@ -694,7 +702,7 @@ describe('ReactPerf', () => {
694702
}
695703
}
696704
class EvilPortal extends React.Component {
697-
componentWillMount() {
705+
componentDidMount() {
698706
var portalContainer = document.createElement('div');
699707
ReactDOM.render(<Evil />, portalContainer);
700708
}

src/renderers/shared/fiber/ReactFiberBeginWork.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ if (__DEV__) {
6666
var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
6767
}
6868

69-
module.exports = function<T, P, I, TI, C, CX>(
70-
config : HostConfig<T, P, I, TI, C, CX>,
69+
module.exports = function<T, P, I, TI, C, CX, CI>(
70+
config : HostConfig<T, P, I, TI, C, CX, CI>,
7171
hostContext : HostContext<C, CX>,
7272
scheduleUpdate : (fiber : Fiber, priorityLevel : PriorityLevel) => void,
7373
getPriorityContext : () => PriorityLevel,

src/renderers/shared/fiber/ReactFiberCommitWork.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ var {
3333
ContentReset,
3434
} = require('ReactTypeOfSideEffect');
3535

36-
module.exports = function<T, P, I, TI, C, CX>(
37-
config : HostConfig<T, P, I, TI, C, CX>,
36+
module.exports = function<T, P, I, TI, C, CX, CI>(
37+
config : HostConfig<T, P, I, TI, C, CX, CI>,
3838
hostContext : HostContext<C, CX>,
3939
captureError : (failedFiber : Fiber, error: Error) => ?Fiber
4040
) {

src/renderers/shared/fiber/ReactFiberCompleteWork.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ if (__DEV__) {
4646
var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
4747
}
4848

49-
module.exports = function<T, P, I, TI, C, CX>(
50-
config : HostConfig<T, P, I, TI, C, CX>,
49+
module.exports = function<T, P, I, TI, C, CX, CI>(
50+
config : HostConfig<T, P, I, TI, C, CX, CI>,
5151
hostContext : HostContext<C, CX>,
5252
) {
5353
const {

src/renderers/shared/fiber/ReactFiberHostContext.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ export type HostContext<C, CX> = {
3434
resetHostContainer() : void,
3535
};
3636

37-
module.exports = function<T, P, I, TI, C, CX>(
38-
config : HostConfig<T, P, I, TI, C, CX>
37+
module.exports = function<T, P, I, TI, C, CX, CI>(
38+
config : HostConfig<T, P, I, TI, C, CX, CI>
3939
) : HostContext<C, CX> {
4040
const {
4141
getChildHostContext,

src/renderers/shared/fiber/ReactFiberReconciler.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export type Deadline = {
4343

4444
type OpaqueNode = Fiber;
4545

46-
export type HostConfig<T, P, I, TI, C, CX> = {
46+
export type HostConfig<T, P, I, TI, C, CX, CI> = {
4747

4848
getRootHostContext(rootContainerInstance : C) : CX,
4949
getChildHostContext(parentHostContext : CX, type : T) : CX,
@@ -68,8 +68,8 @@ export type HostConfig<T, P, I, TI, C, CX> = {
6868
scheduleAnimationCallback(callback : () => void) : void,
6969
scheduleDeferredCallback(callback : (deadline : Deadline) => void) : void,
7070

71-
prepareForCommit() : void,
72-
resetAfterCommit() : void,
71+
prepareForCommit() : CI,
72+
resetAfterCommit(commitInfo : CI) : void,
7373

7474
useSyncScheduling ?: boolean,
7575
};
@@ -99,7 +99,7 @@ getContextForSubtree._injectFiber(function(fiber : Fiber) {
9999
parentContext;
100100
});
101101

102-
module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C, CX>) : Reconciler<C, I, TI> {
102+
module.exports = function<T, P, I, TI, C, CX, CI>(config : HostConfig<T, P, I, TI, C, CX, CI>) : Reconciler<C, I, TI> {
103103

104104
var {
105105
scheduleUpdate,

0 commit comments

Comments
 (0)