main
js 260 lines 6.8 KB
Raw
1 import React, {Component} from 'react';
2 import Draggable from 'react-draggable';
3 import ReactNoop from 'react-noop-renderer';
4 import Editor from './Editor';
5 import Fibers from './Fibers';
6 import describeFibers from './describeFibers';
7
8 // The only place where we use it.
9 const ReactFiberInstrumentation =
10 ReactNoop.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
11 .ReactFiberInstrumentation;
12
13 function getFiberState(root, workInProgress) {
14 if (!root) {
15 return null;
16 }
17 return describeFibers(root.current, workInProgress);
18 }
19
20 const defaultCode = `
21 log('Render <div>Hello</div>');
22 ReactNoop.render(<div>Hello</div>);
23 ReactNoop.flush();
24
25 log('Render <h1>Goodbye</h1>');
26 ReactNoop.render(<h1>Goodbye</h1>);
27 ReactNoop.flush();
28 `;
29
30 class App extends Component {
31 constructor(props) {
32 super(props);
33 this.state = {
34 code: localStorage.getItem('fiber-debugger-code') || defaultCode,
35 isEditing: false,
36 history: [],
37 currentStep: 0,
38 show: {
39 alt: false,
40 child: true,
41 sibling: true,
42 return: false,
43 fx: false,
44 },
45 graphSettings: {
46 rankdir: 'TB',
47 trackActive: true,
48 },
49 };
50 }
51
52 componentDidMount() {
53 this.runCode(this.state.code);
54 }
55
56 runCode(code) {
57 let currentStage;
58 let currentRoot;
59
60 ReactFiberInstrumentation.debugTool = null;
61 ReactNoop.render(null);
62 ReactNoop.flush();
63 ReactFiberInstrumentation.debugTool = {
64 onMountContainer: root => {
65 currentRoot = root;
66 },
67 onUpdateContainer: root => {
68 currentRoot = root;
69 },
70 onBeginWork: fiber => {
71 const fibers = getFiberState(currentRoot, fiber);
72 const stage = currentStage;
73 this.setState(({history}) => ({
74 history: [
75 ...history,
76 {
77 action: 'BEGIN',
78 fibers,
79 stage,
80 },
81 ],
82 }));
83 },
84 onCompleteWork: fiber => {
85 const fibers = getFiberState(currentRoot, fiber);
86 const stage = currentStage;
87 this.setState(({history}) => ({
88 history: [
89 ...history,
90 {
91 action: 'COMPLETE',
92 fibers,
93 stage,
94 },
95 ],
96 }));
97 },
98 onCommitWork: fiber => {
99 const fibers = getFiberState(currentRoot, fiber);
100 const stage = currentStage;
101 this.setState(({history}) => ({
102 history: [
103 ...history,
104 {
105 action: 'COMMIT',
106 fibers,
107 stage,
108 },
109 ],
110 }));
111 },
112 };
113 window.React = React;
114 window.ReactNoop = ReactNoop;
115 window.expect = () => ({
116 toBe() {},
117 toContain() {},
118 toEqual() {},
119 });
120 window.log = s => (currentStage = s);
121 // eslint-disable-next-line
122 eval(
123 window.Babel.transform(code, {
124 presets: ['react', 'es2015'],
125 }).code
126 );
127 }
128
129 handleEdit = e => {
130 e.preventDefault();
131 this.setState({
132 isEditing: true,
133 });
134 };
135
136 handleCloseEdit = nextCode => {
137 localStorage.setItem('fiber-debugger-code', nextCode);
138 this.setState({
139 isEditing: false,
140 history: [],
141 currentStep: 0,
142 code: nextCode,
143 });
144 this.runCode(nextCode);
145 };
146
147 render() {
148 const {history, currentStep, isEditing, code} = this.state;
149 if (isEditing) {
150 return <Editor code={code} onClose={this.handleCloseEdit} />;
151 }
152
153 const {fibers, action, stage} = history[currentStep] || {};
154 let friendlyAction;
155 if (fibers) {
156 let wipFiber = fibers.descriptions[fibers.workInProgressID];
157 let friendlyFiber = wipFiber.type || wipFiber.tag + ' #' + wipFiber.id;
158 friendlyAction = `After ${action} on ${friendlyFiber}`;
159 }
160
161 return (
162 <div style={{height: '100%'}}>
163 {fibers && (
164 <Draggable>
165 <Fibers
166 fibers={fibers}
167 show={this.state.show}
168 graphSettings={this.state.graphSettings}
169 />
170 </Draggable>
171 )}
172 <div
173 style={{
174 width: '100%',
175 textAlign: 'center',
176 position: 'fixed',
177 bottom: 0,
178 padding: 10,
179 zIndex: 1,
180 backgroundColor: '#fafafa',
181 border: '1px solid #ccc',
182 }}>
183 <div style={{width: '50%', float: 'left'}}>
184 <input
185 type="range"
186 style={{width: '25%'}}
187 min={0}
188 max={history.length - 1}
189 value={currentStep}
190 onChange={e =>
191 this.setState({currentStep: Number(e.target.value)})
192 }
193 />
194 <p>
195 Step {currentStep}: {friendlyAction} (
196 <a style={{color: 'gray'}} onClick={this.handleEdit} href="#">
197 Edit
198 </a>
199 )
200 </p>
201 {stage && <p>Stage: {stage}</p>}
202 {Object.keys(this.state.show).map(key => (
203 <label style={{marginRight: '10px'}} key={key}>
204 <input
205 type="checkbox"
206 checked={this.state.show[key]}
207 onChange={e => {
208 this.setState(({show}) => ({
209 show: {...show, [key]: !show[key]},
210 }));
211 }}
212 />
213 {key}
214 </label>
215 ))}
216 </div>
217 <div style={{width: '50%', float: 'right'}}>
218 <h5>Graph Settings</h5>
219 <p>
220 <label style={{display: ''}}>
221 Direction:
222 <select
223 onChange={e => {
224 const rankdir = e.target.value;
225 this.setState(({graphSettings}) => ({
226 graphSettings: {...graphSettings, rankdir},
227 }));
228 }}>
229 <option value="TB">top-down</option>
230 <option value="BT">down-top</option>
231 <option value="LR">left-right</option>
232 <option value="RL">right-left</option>
233 </select>
234 </label>
235 </p>
236 <p>
237 <label style={{marginRight: '10px'}}>
238 <input
239 type="checkbox"
240 checked={this.state.graphSettings.trackActive}
241 onChange={e => {
242 this.setState(({graphSettings}) => ({
243 graphSettings: {
244 ...graphSettings,
245 trackActive: !graphSettings.trackActive,
246 },
247 }));
248 }}
249 />
250 Track active fiber
251 </label>
252 </p>
253 </div>
254 </div>
255 </div>
256 );
257 }
258 }
259
260 export default App;