Skip to content

Commit 4089f5f

Browse files
gaearonacusti
authored andcommitted
Add Fiber Debugger (facebook#8033)
* Build react-noop as a package This lets us consume it from the debugger. * Add instrumentation to Fiber * Check in Fiber Debugger
1 parent a2f99b0 commit 4089f5f

File tree

22 files changed

+6340
-10
lines changed

22 files changed

+6340
-10
lines changed

Gruntfile.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ module.exports = function(grunt) {
9494
grunt.registerTask('npm-react-test:release', npmReactTestRendererTasks.buildRelease);
9595
grunt.registerTask('npm-react-test:pack', npmReactTestRendererTasks.packRelease);
9696

97+
var npmReactNoopRendererTasks = require('./grunt/tasks/npm-react-noop');
98+
grunt.registerTask('npm-react-noop:release', npmReactNoopRendererTasks.buildRelease);
99+
grunt.registerTask('npm-react-noop:pack', npmReactNoopRendererTasks.packRelease);
100+
97101
grunt.registerTask('version-check', function() {
98102
// Use gulp here.
99103
spawnGulp(['version-check'], null, this.async());
@@ -186,6 +190,8 @@ module.exports = function(grunt) {
186190
'npm-react-addons:pack',
187191
'npm-react-test:release',
188192
'npm-react-test:pack',
193+
'npm-react-noop:release',
194+
'npm-react-noop:pack',
189195
'compare_size',
190196
]);
191197

examples/fiber/debugger/.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# See http://help.github.com/ignore-files/ for more about ignoring files.
2+
3+
# dependencies
4+
node_modules
5+
6+
# testing
7+
coverage
8+
9+
# production
10+
build
11+
12+
# misc
13+
.DS_Store
14+
.env
15+
npm-debug.log

examples/fiber/debugger/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Fiber Debugger
2+
3+
This is a debugger handy for visualizing how [Fiber](https://github.com/facebook/react/issues/6170) works internally.
4+
5+
**It is only meant to be used by React contributors, and not by React users.**
6+
7+
It is likely that it might get broken at some point. If it's broken, ping [Dan](https://twitter.com/dan_abramov).
8+
9+
### Running
10+
11+
First, `npm run build` in React root repo folder.
12+
13+
Then `npm install` and `npm start` in this folder.
14+
15+
Open `http://localhost:3000` in Chrome.
16+
17+
### Features
18+
19+
* Edit code that uses `ReactNoop` renderer
20+
* Visualize how relationships between fibers change over time
21+
* Current tree is displayed in green
22+
23+
![fiber debugger](https://d17oy1vhnax1f7.cloudfront.net/items/3R2W1H2M3a0h3p1l133r/Screen%20Recording%202016-10-21%20at%2020.41.gif?v=e4323e51)
24+
25+

examples/fiber/debugger/package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "react-fiber-debugger",
3+
"version": "0.0.1",
4+
"private": true,
5+
"devDependencies": {
6+
"react-scripts": "0.6.1"
7+
},
8+
"dependencies": {
9+
"dagre": "^0.7.4",
10+
"pretty-format": "^4.2.1",
11+
"react": "^15.3.2",
12+
"react-dom": "^15.3.2",
13+
"react-draggable": "^2.2.2",
14+
"react-motion": "^0.4.5"
15+
},
16+
"scripts": {
17+
"start": "react-scripts start",
18+
"build": "react-scripts build"
19+
}
20+
}
24.3 KB
Binary file not shown.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
7+
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.14.0/babel.min.js"></script>
8+
<title>React App</title>
9+
</head>
10+
<body>
11+
<div id="root"></div>
12+
</body>
13+
</html>

examples/fiber/debugger/src/App.js

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import React, { Component } from 'react';
2+
import Draggable from 'react-draggable';
3+
import ReactNoop from '../../../../build/packages/react-noop-renderer';
4+
import ReactFiberInstrumentation from '../../../../build/packages/react-noop-renderer/lib/ReactFiberInstrumentation';
5+
import Editor from './Editor';
6+
import Fibers from './Fibers';
7+
import describeFibers from './describeFibers';
8+
9+
function getFiberState(root, workInProgress) {
10+
if (!root) {
11+
return null;
12+
}
13+
return describeFibers(root.current, workInProgress);
14+
}
15+
16+
const defaultCode = `
17+
log('Render <div>Hello</div>');
18+
ReactNoop.render(<div>Hello</div>);
19+
ReactNoop.flush();
20+
21+
log('Render <h1>Goodbye</h1>');
22+
ReactNoop.render(<h1>Goodbye</h1>);
23+
ReactNoop.flush();
24+
`;
25+
26+
class App extends Component {
27+
constructor(props) {
28+
super(props);
29+
this.state = {
30+
code: defaultCode,
31+
isEditing: false,
32+
history: [],
33+
currentStep: 0,
34+
show: {
35+
alt: false,
36+
child: true,
37+
sibling: true,
38+
return: false,
39+
fx: false,
40+
progressedChild: false,
41+
progressedDel: false
42+
}
43+
};
44+
}
45+
46+
componentDidMount() {
47+
this.runCode(this.state.code);
48+
}
49+
50+
runCode(code) {
51+
let currentStage;
52+
let currentRoot;
53+
54+
ReactFiberInstrumentation.debugTool = {
55+
onMountContainer: (root) => {
56+
currentRoot = root;
57+
},
58+
onUpdateContainer: (root) => {
59+
currentRoot = root;
60+
},
61+
onWillBeginWork: (fiber) => {
62+
const fibers = getFiberState(currentRoot, fiber);
63+
const stage = currentStage;
64+
this.setState(({ history }) => ({
65+
history: [
66+
...history, {
67+
action: 'willBeginWork',
68+
fibers,
69+
stage
70+
}
71+
]
72+
}));
73+
},
74+
onDidBeginWork: (fiber) => {
75+
const fibers = getFiberState(currentRoot, fiber);
76+
const stage = currentStage;
77+
this.setState(({ history }) => ({
78+
history: [
79+
...history, {
80+
action: 'didBeginWork',
81+
fibers,
82+
stage
83+
}
84+
]
85+
}));
86+
},
87+
onWillCompleteWork: (fiber) => {
88+
const fibers = getFiberState(currentRoot, fiber);
89+
const stage = currentStage;
90+
this.setState(({ history }) => ({
91+
history: [
92+
...history, {
93+
action: 'willCompleteWork',
94+
fibers,
95+
stage
96+
}
97+
]
98+
}));
99+
},
100+
onDidCompleteWork: (fiber) => {
101+
const fibers = getFiberState(currentRoot, fiber);
102+
const stage = currentStage;
103+
this.setState(({ history }) => ({
104+
history: [
105+
...history, {
106+
action: 'didCompleteWork',
107+
fibers,
108+
stage
109+
}
110+
]
111+
}));
112+
},
113+
};
114+
window.React = React;
115+
window.ReactNoop = ReactNoop;
116+
window.log = s => currentStage = s;
117+
// eslint-disable-next-line
118+
eval(window.Babel.transform(code, {
119+
presets: ['react', 'es2015']
120+
}).code);
121+
}
122+
123+
handleEdit = (e) => {
124+
e.preventDefault();
125+
this.setState({
126+
isEditing: true
127+
});
128+
}
129+
130+
handleCloseEdit = (nextCode) => {
131+
this.setState({
132+
isEditing: false,
133+
history: [],
134+
currentStep: 0,
135+
code: nextCode
136+
});
137+
this.runCode(nextCode);
138+
}
139+
140+
render() {
141+
const { history, currentStep, isEditing, code } = this.state;
142+
if (isEditing) {
143+
return <Editor code={code} onClose={this.handleCloseEdit} />;
144+
}
145+
146+
const { fibers, action, stage } = history[currentStep] || {};
147+
let friendlyAction;
148+
149+
if (fibers) {
150+
let wipFiber = fibers.descriptions[fibers.workInProgressID];
151+
let friendlyFiber = wipFiber.type || wipFiber.tag + ' #' + wipFiber.id;
152+
switch (action) {
153+
case 'willBeginWork':
154+
friendlyAction = 'Before BEGIN phase on ' + friendlyFiber;
155+
break;
156+
case 'didBeginWork':
157+
friendlyAction = 'After BEGIN phase on ' + friendlyFiber;
158+
break;
159+
case 'willCompleteWork':
160+
friendlyAction = 'Before COMPLETE phase on ' + friendlyFiber;
161+
break;
162+
case 'didCompleteWork':
163+
friendlyAction = 'After COMPLETE phase on ' + friendlyFiber;
164+
break;
165+
default:
166+
throw new Error('Unknown action');
167+
}
168+
}
169+
170+
return (
171+
<div style={{ height: '100%' }}>
172+
{fibers &&
173+
<Draggable>
174+
<Fibers fibers={fibers} show={this.state.show} />
175+
</Draggable>
176+
}
177+
<div style={{
178+
width: '100%',
179+
textAlign: 'center',
180+
position: 'fixed',
181+
bottom: 0,
182+
padding: 10,
183+
zIndex: 1,
184+
backgroundColor: '#fafafa',
185+
border: '1px solid #ccc'
186+
}}>
187+
<input
188+
type="range"
189+
min={0}
190+
max={history.length - 1}
191+
value={currentStep}
192+
onChange={e => this.setState({ currentStep: Number(e.target.value) })}
193+
/>
194+
<p>Step {currentStep}: {friendlyAction} (<a style={{ color: 'gray' }} onClick={this.handleEdit} href='#'>Edit</a>)</p>
195+
{stage && <p>Stage: {stage}</p>}
196+
{Object.keys(this.state.show).map(key =>
197+
<label style={{ marginRight: '10px' }} key={key}>
198+
<input
199+
type="checkbox"
200+
checked={this.state.show[key]}
201+
onChange={e => {
202+
this.setState(({ show }) => ({
203+
show: {...show, [key]: !show[key]}
204+
}));
205+
}} />
206+
{key}
207+
</label>
208+
)}
209+
</div>
210+
</div>
211+
);
212+
}
213+
}
214+
215+
export default App;

examples/fiber/debugger/src/Editor.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React, { Component } from 'react';
2+
3+
class Editor extends Component {
4+
constructor(props) {
5+
super(props);
6+
this.state = {
7+
code: props.code
8+
};
9+
}
10+
11+
render() {
12+
return (
13+
<div style={{
14+
height: '100%',
15+
width: '100%'
16+
}}>
17+
<textarea
18+
value={this.state.code}
19+
onChange={e => this.setState({ code: e.target.value })}
20+
style={{
21+
height: '80%',
22+
width: '100%',
23+
fontSize: '15px'
24+
}} />
25+
<div style={{ height: '20%', textAlign: 'center' }}>
26+
<button onClick={() => this.props.onClose(this.state.code)} style={{ fontSize: 'large' }}>
27+
Run
28+
</button>
29+
</div>
30+
</div>
31+
)
32+
}
33+
}
34+
35+
export default Editor;

0 commit comments

Comments
 (0)