Improve Fiber debugger (#8767)
* Simplify the hooks * Capture completion at the right moment * Animate the scroll with the fiber on the stack * Better display priorities
Dan Abramov committed
Jan 13, 2017 at 04:41 UTC
5afd8cd704356cf7928ed6a1763e8fab6e496032
3 files changed
+96
-70
examples/fiber/debugger/src/App.js
+13
-36
@@ -61,52 +61,39 @@ class App extends Component {
61
onUpdateContainer: (root) => {
62
currentRoot = root;
63
},
64
- onWillBeginWork: (fiber) => {
64
+ onBeginWork: (fiber) => {
65
const fibers = getFiberState(currentRoot, fiber);
66
const stage = currentStage;
67
this.setState(({ history }) => ({
68
history: [
69
...history, {
70
- action: 'willBeginWork',
70
+ action: 'BEGIN',
71
fibers,
72
stage
73
}
74
]
75
}));
76
},
77
- onDidBeginWork: (fiber) => {
77
+ onCompleteWork: (fiber) => {
78
const fibers = getFiberState(currentRoot, fiber);
79
const stage = currentStage;
80
this.setState(({ history }) => ({
81
history: [
82
...history, {
83
- action: 'didBeginWork',
83
+ action: 'COMPLETE',
84
fibers,
85
stage
86
}
87
]
88
}));
89
},
90
- onWillCompleteWork: (fiber) => {
90
+ onCommitWork: (fiber) => {
91
const fibers = getFiberState(currentRoot, fiber);
92
const stage = currentStage;
93
this.setState(({ history }) => ({
94
history: [
95
...history, {
96
- action: 'willCompleteWork',
97
- fibers,
98
- stage
99
- }
100
- ]
101
- }));
102
- },
103
- onDidCompleteWork: (fiber) => {
104
- const fibers = getFiberState(currentRoot, fiber);
105
- const stage = currentStage;
106
- this.setState(({ history }) => ({
107
- history: [
108
- ...history, {
109
- action: 'didCompleteWork',
96
+ action: 'COMMIT',
97
fibers,
98
stage
99
}
@@ -116,6 +103,11 @@ class App extends Component {
103
};
104
window.React = React;
105
window.ReactNoop = ReactNoop;
106
+ window.expect = () => ({
107
+ toBe() {},
108
+ toContain() {},
109
+ toEqual() {},
110
+ });
111
window.log = s => currentStage = s;
112
// eslint-disable-next-line
113
eval(window.Babel.transform(code, {
@@ -149,26 +141,10 @@ class App extends Component {
141
142
const { fibers, action, stage } = history[currentStep] || {};
143
let friendlyAction;
152
-
144
if (fibers) {
145
let wipFiber = fibers.descriptions[fibers.workInProgressID];
146
let friendlyFiber = wipFiber.type || wipFiber.tag + ' #' + wipFiber.id;
156
- switch (action) {
157
- case 'willBeginWork':
158
- friendlyAction = 'Before BEGIN phase on ' + friendlyFiber;
159
- break;
160
- case 'didBeginWork':
161
- friendlyAction = 'After BEGIN phase on ' + friendlyFiber;
162
- break;
163
- case 'willCompleteWork':
164
- friendlyAction = 'Before COMPLETE phase on ' + friendlyFiber;
165
- break;
166
- case 'didCompleteWork':
167
- friendlyAction = 'After COMPLETE phase on ' + friendlyFiber;
168
- break;
169
- default:
170
- throw new Error('Unknown action');
171
- }
147
+ friendlyAction = `After ${action} on ${friendlyFiber}`;
148
}
149
150
return (
@@ -190,6 +166,7 @@ class App extends Component {
166
}}>
167
<input
168
type="range"
169
+ style={{ width: '25%' }}
170
min={0}
171
max={history.length - 1}
172
value={currentStep}
examples/fiber/debugger/src/Fibers.js
+71
-9
@@ -58,17 +58,26 @@ function Graph(props) {
58
59
dagre.layout(g);
60
61
+ var activeNode = g.nodes().map(v => g.node(v)).find(node =>
62
+ node.label.props.isActive
63
+ );
64
+ const [winX, winY] = [window.innerWidth / 2, window.innerHeight / 2]
65
+ var focusDx = activeNode ? (winX - activeNode.x) : 0;
66
+ var focusDy = activeNode ? (winY - activeNode.y) : 0;
67
+
68
var nodes = g.nodes().map(v => {
69
var node = g.node(v);
70
return (
71
<Motion style={{
65
- x: props.isDragging ? node.x : spring(node.x),
66
- y: props.isDragging ? node.y : spring(node.y)
72
+ x: props.isDragging ? node.x + focusDx : spring(node.x + focusDx),
73
+ y: props.isDragging ? node.y + focusDy : spring(node.y + focusDy)
74
}} key={node.label.key}>
75
{interpolatingStyle =>
76
React.cloneElement(node.label, {
77
x: interpolatingStyle.x + props.dx,
71
- y: interpolatingStyle.y + props.dy
78
+ y: interpolatingStyle.y + props.dy,
79
+ vanillaX: node.x,
80
+ vanillaY: node.y,
81
})
82
}
83
</Motion>
@@ -80,8 +89,8 @@ function Graph(props) {
89
let idx = 0;
90
return (
91
<Motion style={edge.points.reduce((bag, point) => {
83
- bag[idx + ':x'] = props.isDragging ? point.x : spring(point.x);
84
- bag[idx + ':y'] = props.isDragging ? point.y : spring(point.y);
92
+ bag[idx + ':x'] = props.isDragging ? point.x + focusDx : spring(point.x + focusDx);
93
+ bag[idx + ':y'] = props.isDragging ? point.y + focusDy : spring(point.y + focusDy);
94
idx++;
95
return bag;
96
}, {})} key={edge.label.key}>
@@ -131,7 +140,9 @@ function Vertex(props) {
140
overflow: 'hidden',
141
padding: '4px',
142
wordWrap: 'break-word'
134
- }} {...props} />
143
+ }}>
144
+ {props.children}
145
+ </div>
146
);
147
}
148
Vertex.isVertex = true;
@@ -196,6 +207,25 @@ function Edge(props) {
207
}
208
Edge.isEdge = true;
209
210
+function formatPriority(priority) {
211
+ switch (priority) {
212
+ case 1:
213
+ return 'synchronous';
214
+ case 2:
215
+ return 'task';
216
+ case 3:
217
+ return 'animation';
218
+ case 4:
219
+ return 'hi-pri work';
220
+ case 5:
221
+ return 'lo-pri work';
222
+ case 6:
223
+ return 'offscreen work';
224
+ default:
225
+ throw new Error('Unknown priority.');
226
+ }
227
+}
228
+
229
export default function Fibers({ fibers, show, ...rest }) {
230
const items = Object.keys(fibers.descriptions).map(id =>
231
fibers.descriptions[id]
@@ -216,12 +246,18 @@ export default function Fibers({ fibers, show, ...rest }) {
246
...rest.style,
247
transform: null
248
}}>
219
- <Graph className="graph" dx={dx} dy={dy} isDragging={isDragging}>
249
+ <Graph
250
+ className="graph"
251
+ dx={dx}
252
+ dy={dy}
253
+ isDragging={isDragging}
254
+ >
255
{items.map(fiber => [
256
<Vertex
257
key={fiber.id}
258
width={200}
224
- height={100}>
259
+ height={100}
260
+ isActive={fiber.id === fibers.workInProgressID}>
261
<div
262
style={{
263
width: '100%',
@@ -233,7 +269,33 @@ export default function Fibers({ fibers, show, ...rest }) {
269
<br />
270
{fiber.type}
271
<br />
236
- <small>Priority: {fiber.pendingWorkPriority} pend, {fiber.progressedPriority} prog</small>
272
+ {fibers.currentIDs.indexOf(fiber.id) === -1 ?
273
+ <small>
274
+ {fiber.pendingWorkPriority !== 0 && [
275
+ <span style={{
276
+ fontWeight: fiber.pendingWorkPriority <= fiber.progressedPriority ?
277
+ 'bold' :
278
+ 'normal'
279
+ }}>
280
+ Needs: {formatPriority(fiber.pendingWorkPriority)}
281
+ </span>,
282
+ <br />
283
+ ]}
284
+ {fiber.progressedPriority !== 0 && [
285
+ `Finished: ${formatPriority(fiber.progressedPriority)}`,
286
+ <br />
287
+ ]}
288
+ {fiber.memoizedProps !== null && fiber.pendingProps !== null && [
289
+ fiber.memoizedProps === fiber.pendingProps ?
290
+ 'Can reuse memoized.' :
291
+ 'Cannot reuse memoized.',
292
+ <br />
293
+ ]}
294
+ </small> :
295
+ <small>
296
+ Committed
297
+ </small>
298
+ }
299
</div>
300
</Vertex>,
301
fiber.child && show.child &&
src/renderers/shared/fiber/ReactFiberScheduler.js
+12
-25
@@ -396,6 +396,9 @@ module.exports = function<T, P, I, TI, PI, C, CX>(config : HostConfig<T, P, I, T
396
}
397
398
isCommitting = false;
399
+ if (__DEV__ && ReactFiberInstrumentation.debugTool) {
400
+ ReactFiberInstrumentation.debugTool.onCommitWork(finishedWork);
401
+ }
402
403
// If we caught any errors during this commit, schedule their boundaries
404
// to update.
@@ -448,6 +451,9 @@ module.exports = function<T, P, I, TI, PI, C, CX>(config : HostConfig<T, P, I, T
451
resetWorkPriority(workInProgress);
452
453
if (next) {
454
+ if (__DEV__ && ReactFiberInstrumentation.debugTool) {
455
+ ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
456
+ }
457
// If completing this work spawned new work, do that next. We'll come
458
// back here again.
459
return next;
@@ -483,6 +489,10 @@ module.exports = function<T, P, I, TI, PI, C, CX>(config : HostConfig<T, P, I, T
489
}
490
}
491
492
+ if (__DEV__ && ReactFiberInstrumentation.debugTool) {
493
+ ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
494
+ }
495
+
496
if (siblingFiber) {
497
// If there is more work to do in this returnFiber, do that next.
498
return siblingFiber;
@@ -507,32 +517,21 @@ module.exports = function<T, P, I, TI, PI, C, CX>(config : HostConfig<T, P, I, T
517
}
518
519
function performUnitOfWork(workInProgress : Fiber) : ?Fiber {
510
-
520
// The current, flushed, state of this fiber is the alternate.
521
// Ideally nothing should rely on this, but relying on it here
522
// means that we don't need an additional field on the work in
523
// progress.
524
const current = workInProgress.alternate;
525
517
- if (__DEV__ && ReactFiberInstrumentation.debugTool) {
518
- ReactFiberInstrumentation.debugTool.onWillBeginWork(workInProgress);
519
- }
526
// See if beginning this work spawns more work.
527
let next = beginWork(current, workInProgress, nextPriorityLevel);
522
-
528
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
524
- ReactFiberInstrumentation.debugTool.onDidBeginWork(workInProgress);
529
+ ReactFiberInstrumentation.debugTool.onBeginWork(workInProgress);
530
}
531
532
if (!next) {
528
- if (__DEV__ && ReactFiberInstrumentation.debugTool) {
529
- ReactFiberInstrumentation.debugTool.onWillCompleteWork(workInProgress);
530
- }
533
// If this doesn't spawn new work, complete the current work.
534
next = completeUnitOfWork(workInProgress);
533
- if (__DEV__ && ReactFiberInstrumentation.debugTool) {
534
- ReactFiberInstrumentation.debugTool.onDidCompleteWork(workInProgress);
535
- }
535
}
536
537
ReactCurrentOwner.current = null;
@@ -540,8 +539,6 @@ module.exports = function<T, P, I, TI, PI, C, CX>(config : HostConfig<T, P, I, T
539
ReactDebugCurrentFiber.current = null;
540
}
541
543
-
544
-
542
return next;
543
}
544
@@ -553,25 +550,15 @@ module.exports = function<T, P, I, TI, PI, C, CX>(config : HostConfig<T, P, I, T
550
// progress.
551
const current = workInProgress.alternate;
552
556
- if (__DEV__ && ReactFiberInstrumentation.debugTool) {
557
- ReactFiberInstrumentation.debugTool.onWillBeginWork(workInProgress);
558
- }
553
// See if beginning this work spawns more work.
554
let next = beginFailedWork(current, workInProgress, nextPriorityLevel);
561
-
555
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
563
- ReactFiberInstrumentation.debugTool.onDidBeginWork(workInProgress);
556
+ ReactFiberInstrumentation.debugTool.onBeginWork(workInProgress);
557
}
558
559
if (!next) {
567
- if (__DEV__ && ReactFiberInstrumentation.debugTool) {
568
- ReactFiberInstrumentation.debugTool.onWillCompleteWork(workInProgress);
569
- }
560
// If this doesn't spawn new work, complete the current work.
561
next = completeUnitOfWork(workInProgress);
572
- if (__DEV__ && ReactFiberInstrumentation.debugTool) {
573
- ReactFiberInstrumentation.debugTool.onDidCompleteWork(workInProgress);
574
- }
562
}
563
564
ReactCurrentOwner.current = null;