Skip to content

Commit e65667e

Browse files
committed
Protect against render loops caused by errors
1 parent 878e6bf commit e65667e

File tree

2 files changed

+18
-8
lines changed

2 files changed

+18
-8
lines changed

src/render/classComponent.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@ import {
2424
getCurrentErrorFrame
2525
} from '../internals'
2626

27+
const RE_RENDER_LIMIT = 25
28+
2729
const createUpdater = () => {
2830
const queue = []
2931

3032
return {
33+
_thrown: 0,
3134
queue,
3235
isMounted: () => false,
3336
enqueueForceUpdate: () => null,
@@ -190,12 +193,21 @@ export const update = (queue: Frame[], frame: ClassFrame) => {
190193
setCurrentErrorFrame(frame.errorFrame)
191194

192195
if (frame.error) {
193-
if (typeof frame.instance.componentDidCatch === 'function')
196+
// We simply have to bail when a loop occurs
197+
if (++frame.instance.updater._thrown >= RE_RENDER_LIMIT) return null
198+
199+
frame.instance._isMounted = true
200+
201+
if (typeof frame.instance.componentDidCatch === 'function') {
194202
frame.instance.componentDidCatch(frame.error)
195-
if (typeof frame.type.getDerivedStateFromError === 'function')
203+
}
204+
205+
if (typeof frame.type.getDerivedStateFromError === 'function') {
196206
frame.instance.updater.enqueueSetState(
207+
frame.instance,
197208
frame.type.getDerivedStateFromError(frame.error)
198209
)
210+
}
199211
}
200212

201213
return render(frame.type, frame.instance, queue)

src/visitor.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ const visitLoop = (
218218
const errorFrame = getCurrentErrorFrame()
219219
if (!errorFrame) throw error
220220
errorFrame.error = error
221-
queue.push(errorFrame)
221+
queue.unshift(errorFrame)
222222
return false
223223
} finally {
224224
ReactCurrentDispatcher.current = prevDispatcher
@@ -247,8 +247,6 @@ export const visit = (
247247
queue: Frame[],
248248
visitor: Visitor
249249
) => {
250-
if (!init.length) return
251-
252250
const traversalChildren: AbstractElement[][] = [init]
253251
const traversalMap: Array<void | ContextMap> = [flushPrevContextMap()]
254252
const traversalStore: Array<void | ContextEntry> = [flushPrevContextStore()]
@@ -305,9 +303,9 @@ export const update = (frame: Frame, queue: Frame[], visitor: Visitor) => {
305303
const prevDispatcher = ReactCurrentDispatcher.current
306304
let children = null
307305

308-
try {
309-
ReactCurrentDispatcher.current = Dispatcher
306+
ReactCurrentDispatcher.current = Dispatcher
310307

308+
try {
311309
if (frame.kind === 'frame.class') {
312310
children = updateClassComponent(queue, frame)
313311
} else if (frame.kind === 'frame.hooks') {
@@ -319,7 +317,7 @@ export const update = (frame: Frame, queue: Frame[], visitor: Visitor) => {
319317
const errorFrame = getCurrentErrorFrame()
320318
if (!errorFrame) throw error
321319
errorFrame.error = error
322-
queue.push(errorFrame)
320+
queue.unshift(errorFrame)
323321
children = null
324322
} finally {
325323
ReactCurrentDispatcher.current = prevDispatcher

0 commit comments

Comments
 (0)