Skip to content

Commit 2d53e59

Browse files
committed
Clean up backend. Need to work on how to consolidate state with frontend
1 parent 8ed1dd5 commit 2d53e59

File tree

6 files changed

+136
-168
lines changed

6 files changed

+136
-168
lines changed

demo-app/src/client/Components/Buttons.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ function Buttons() {
1515
hook.
1616
</h4>
1717
{buttons}
18-
{buttons}
1918
</div>
2019
);
2120
}

demo-app/src/client/Components/Increment.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,6 @@ function Increment() {
1010
<button className='increment' onClick={() => setCount(count + 1)}>
1111
You clicked me {count} times.
1212
</button>
13-
{/* <div> */}
14-
<Box
15-
value={value}
16-
column={3}
17-
row={2}
18-
handleBoxClick={() => setValue((value: BoardText) => (value == 'X' ? 'O' : 'X'))}
19-
/>
20-
{/* </div> */}
2113
</div>
2214
);
2315
}

src/backend/controllers/createTree/createComponentActionsRecord.ts

Lines changed: 122 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,16 @@ import {
77
FunctionComponent,
88
ClassComponent,
99
IndeterminateComponent, // Before we know whether it is function or class
10-
HostRoot, // Root of a host tree. Could be nested inside another node.
11-
HostPortal, // A subtree. Could be an entry point to a different renderer.
12-
/**
13-
* Host Component: a type of component that represents a native DOM element in the browser environment, such as div, span, input, h1 etc.
14-
*/
15-
HostComponent, // has stateNode of html elements
16-
HostText,
17-
Fragment,
18-
Mode,
19-
ContextConsumer,
2010
ContextProvider,
21-
ForwardRef,
22-
Profiler,
23-
SuspenseComponent,
24-
MemoComponent,
25-
SimpleMemoComponent, // A higher order component where if the component renders the same result given the same props, react skips rendering the component and uses last rendered result. Has memoizedProps/memoizedState but no stateNode
26-
LazyComponent,
27-
IncompleteClassComponent,
28-
DehydratedFragment,
29-
SuspenseListComponent,
30-
FundamentalComponent,
31-
ScopeComponent,
32-
Block,
33-
OffscreenComponent,
34-
LegacyHiddenComponent,
3511
} from '../../types/backendTypes';
36-
// import function that creates a tree
37-
import Tree from '../../models/tree';
3812
// passes the data down to its components
3913
import componentActionsRecord from '../../models/masterState';
4014
import { getHooksStateAndUpdateMethod } from './statePropExtractors';
41-
import { nextJSDefaultComponent, remixDefaultComponents } from '../../models/filterConditions';
15+
import {
16+
nextJSDefaultComponent,
17+
remixDefaultComponents,
18+
allowedComponentTypes,
19+
} from '../../models/filterConditions';
4220

4321
// ------------------------CREATE COMPONENT ACTIONS RECORD----------------------
4422
/**
@@ -48,91 +26,127 @@ import { nextJSDefaultComponent, remixDefaultComponents } from '../../models/fil
4826
* @param currentFiberNode A Fiber object
4927
* @param circularComponentTable A table content visited Fiber nodes
5028
*/
51-
export default function createComponentActionsRecord(
52-
currentFiberNode: Fiber,
53-
circularComponentTable: Set<Fiber> = new Set(),
54-
): void {
55-
// Base Case: if has visited, return
56-
if (circularComponentTable.has(currentFiberNode)) {
57-
return;
58-
} else {
59-
circularComponentTable.add(currentFiberNode);
60-
}
61-
const {
62-
sibling,
63-
stateNode,
64-
child,
65-
// with memoizedState we can grab the root type and construct an Abstract Syntax Tree from the hooks structure using Acorn in order to extract the hook getters and match them with their corresponding setters in an object
66-
memoizedState,
67-
elementType,
68-
tag,
69-
} = currentFiberNode;
70-
// Obtain component name:
71-
const componentName =
72-
elementType?._context?.displayName || //For ContextProvider
73-
elementType?._result?.name || //For lazy Component
74-
elementType?.render?.name ||
75-
elementType?.name ||
76-
'nameless';
77-
// console.log('createComponentActionsRecord', {
78-
// currentFiberNode,
79-
// // tag,
80-
// // elementType,
81-
// componentName:
82-
// elementType?._context?.displayName || //For ContextProvider
83-
// elementType?._result?.name || //For lazy Component
84-
// elementType?.render?.name ||
85-
// elementType?.name ||
86-
// elementType,
87-
// // memoizedState,
88-
// // stateNode,
89-
// // _debugHookTypes,
90-
// });
29+
export default function createComponentActionsRecord(currentFiberNode: Fiber): void {
30+
const circularComponentTable: Set<Fiber> = new Set();
31+
_createComponentActionsRecord(currentFiberNode);
9132

92-
// ---------OBTAIN STATE & SET STATE METHODS FROM CLASS COMPONENT-----------
93-
// Check if node is a stateful class component when user use setState.
94-
// If user use setState to define/manage state, the state object will be stored in stateNode.state => grab the state object stored in the stateNode.state
95-
// Example: for tic-tac-toe demo-app: Board is a stateful component that use setState to store state data.
96-
if (
97-
!nextJSDefaultComponent.has(componentName) &&
98-
!remixDefaultComponents.has(componentName) &&
99-
stateNode?.state &&
100-
(tag === ClassComponent || tag === IndeterminateComponent)
101-
) {
102-
// Save component setState() method to our componentActionsRecord for use during timeJump
103-
componentActionsRecord.saveNew(stateNode);
104-
}
33+
/**
34+
* This is a helper function to recursively traverse the React Fiber Tree and craft the snapshot tree to send to front end
35+
* @param currentFiberNode A Fiber object
36+
*/
37+
function _createComponentActionsRecord(currentFiberNode: Fiber): void {
38+
// ----------------------UPDATE VISITED FIBER NODE SET----------------------
39+
// Base Case: if has visited the component, return
40+
if (circularComponentTable.has(currentFiberNode)) {
41+
return;
42+
} else {
43+
circularComponentTable.add(currentFiberNode);
44+
}
45+
46+
// ------------------OBTAIN DATA FROM THE CURRENT FIBER NODE----------------
47+
// Destructure the current fiber node:
48+
const {
49+
sibling,
50+
stateNode,
51+
child,
52+
// with memoizedState we can grab the root type and construct an Abstract Syntax Tree from the hooks structure using Acorn in order to extract the hook getters and match them with their corresponding setters in an object
53+
memoizedState,
54+
elementType,
55+
tag,
56+
} = currentFiberNode;
57+
58+
// Obtain component name:
59+
const componentName =
60+
elementType?._context?.displayName || //For ContextProvider
61+
elementType?._result?.name || //For lazy Component
62+
elementType?.render?.name ||
63+
elementType?.name ||
64+
'nameless';
10565

106-
// --------OBTAIN STATE & DISPATCH METHODS FROM FUNCTIONAL COMPONENT--------
107-
// Check if node is a stateful class component when user use setState.
108-
// If user use useState to define/manage state, the state object will be stored in memoizedState.queue => grab the state object stored in the memoizedState.queue
109-
if (
110-
!nextJSDefaultComponent.has(componentName) &&
111-
!remixDefaultComponents.has(componentName) &&
112-
memoizedState &&
113-
(tag === FunctionComponent || tag === IndeterminateComponent || tag === ContextProvider) //TODO: Need to figure out why we need context provider
114-
) {
115-
if (memoizedState.queue) {
116-
try {
117-
// Hooks states are stored as a linked list using memoizedState.next,
118-
// so we must traverse through the list and get the states.
119-
// We then store them along with the corresponding memoizedState.queue,
120-
// which includes the dispatch() function we use to change their state.
121-
const hooksStates = getHooksStateAndUpdateMethod(memoizedState);
122-
hooksStates.forEach(({ component }) => {
123-
// Save component's state and dispatch() function to our record for future time-travel state changing. Add record index to snapshot so we can retrieve.
124-
componentActionsRecord.saveNew(component);
125-
});
126-
} catch (err) {
127-
console.log('Failed Element', { component: elementType?.name });
66+
// console.log('createComponentActionsRecord', {
67+
// currentFiberNode,
68+
// // tag,
69+
// // elementType,
70+
// componentName:
71+
// elementType?._context?.displayName || //For ContextProvider
72+
// elementType?._result?.name || //For lazy Component
73+
// elementType?.render?.name ||
74+
// elementType?.name ||
75+
// elementType,
76+
// // memoizedState,
77+
// // stateNode,
78+
// // _debugHookTypes,
79+
// });
80+
81+
// --------------------FILTER COMPONENTS/FIBER NODE-------------------------
82+
/**
83+
* For the snapshot tree,
84+
* 1. We will only interested in components that are one of these types: Function Component, Class Component, Indeterminate Component or Context Provider.
85+
* NOTE: this list of components may change depending on future use
86+
* 2. If user use Next JS, filter out default NextJS components
87+
* 3. If user use Remix JS, filter out default Remix components
88+
*/
89+
90+
if (
91+
!allowedComponentTypes.has(tag) ||
92+
nextJSDefaultComponent.has(componentName) ||
93+
remixDefaultComponents.has(componentName)
94+
) {
95+
// -------------------TRAVERSE TO NEXT FIBERNODE------------------------
96+
// If currentFiberNode has children, recurse on children
97+
if (child) _createComponentActionsRecord(child);
98+
99+
// If currentFiberNode has siblings, recurse on siblings
100+
if (sibling) {
101+
_createComponentActionsRecord(sibling);
128102
}
103+
// ---------RETURN THE TREE OUTPUT & PASS TO FRONTEND FOR RENDERING-------
104+
return;
105+
}
106+
107+
// ---------OBTAIN STATE & SET STATE METHODS FROM CLASS COMPONENT-----------
108+
// Check if node is a stateful class component when user use setState.
109+
// If user use setState to define/manage state, the state object will be stored in stateNode.state => grab the state object stored in the stateNode.state
110+
// Example: for tic-tac-toe demo-app: Board is a stateful component that use setState to store state data.
111+
if ((tag === ClassComponent || tag === IndeterminateComponent) && stateNode?.state) {
112+
// Save component setState() method to our componentActionsRecord for use during timeJump
113+
componentActionsRecord.saveNew(stateNode);
129114
}
130-
}
131115

132-
// ---------------------TRAVERSE TO NEXT FIBERNODE--------------------------
133-
// If currentFiberNode has children, recurse on children
134-
if (child) createComponentActionsRecord(child, circularComponentTable);
116+
// --------OBTAIN STATE & DISPATCH METHODS FROM FUNCTIONAL COMPONENT--------
117+
// Check if node is a stateful class component when user use setState.
118+
// If user use useState to define/manage state, the state object will be stored in memoizedState.queue => grab the state object stored in the memoizedState.queue
119+
if (
120+
(tag === FunctionComponent ||
121+
tag === IndeterminateComponent ||
122+
//TODO: Need to figure out why we need context provider
123+
tag === ContextProvider) &&
124+
memoizedState
125+
) {
126+
if (memoizedState.queue) {
127+
try {
128+
// Hooks states are stored as a linked list using memoizedState.next,
129+
// so we must traverse through the list and get the states.
130+
// We then store them along with the corresponding memoizedState.queue,
131+
// which includes the dispatch() function we use to change their state.
132+
const hooksStates = getHooksStateAndUpdateMethod(memoizedState);
133+
hooksStates.forEach(({ component }) => {
134+
// Save component's state and dispatch() function to our record for future time-travel state changing. Add record index to snapshot so we can retrieve.
135+
componentActionsRecord.saveNew(component);
136+
});
137+
} catch (err) {
138+
console.log('ERROR: Failed Element during JSX parsing', {
139+
componentName,
140+
});
141+
}
142+
}
143+
}
135144

136-
// If currentFiberNode has siblings, recurse on siblings
137-
if (sibling) createComponentActionsRecord(sibling, circularComponentTable);
145+
// ---------------------TRAVERSE TO NEXT FIBERNODE--------------------------
146+
// If currentFiberNode has children, recurse on children
147+
if (child) _createComponentActionsRecord(child);
148+
149+
// If currentFiberNode has siblings, recurse on siblings
150+
if (sibling) _createComponentActionsRecord(sibling);
151+
}
138152
}

src/backend/controllers/createTree/createTree.ts

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,7 @@ import {
77
FunctionComponent,
88
ClassComponent,
99
IndeterminateComponent, // Before we know whether it is function or class
10-
HostRoot, // Root of a host tree. Could be nested inside another node.
11-
HostPortal, // A subtree. Could be an entry point to a different renderer.
12-
/**
13-
* Host Component: a type of component that represents a native DOM element in the browser environment, such as div, span, input, h1 etc.
14-
*/
15-
HostComponent, // has stateNode of html elements
16-
HostText,
17-
Fragment,
18-
Mode,
19-
ContextConsumer,
2010
ContextProvider,
21-
ForwardRef,
22-
Profiler,
23-
SuspenseComponent,
24-
MemoComponent,
25-
SimpleMemoComponent, // A higher order component where if the component renders the same result given the same props, react skips rendering the component and uses last rendered result. Has memoizedProps/memoizedState but no stateNode
26-
LazyComponent,
27-
IncompleteClassComponent,
28-
DehydratedFragment,
29-
SuspenseListComponent,
30-
FundamentalComponent,
31-
ScopeComponent,
32-
Block,
33-
OffscreenComponent,
34-
LegacyHiddenComponent,
3511
} from '../../types/backendTypes';
3612
// import function that creates a tree
3713
import Tree from '../../models/tree';
@@ -57,7 +33,6 @@ import {
5733
* 3. Build a new state snapshot
5834
* Every time a state change is made in the accompanying app, the extension creates a Tree “snapshot” of the current state, and adds it to the current “cache” of snapshots in the extension
5935
* @param currentFiberNode A Fiber object
60-
* @param tree A Tree object, default initialized to an instance given 'root' and 'root'
6136
* @return An instance of a Tree object
6237
*/
6338
// TODO: Not sure why the ritd need to be outside of the createTree function. Want to put inside, but in case this need to be keep track for front end.
@@ -80,6 +55,7 @@ export default function createTree(currentFiberNode: Fiber): Tree {
8055
} else {
8156
circularComponentTable.add(currentFiberNode);
8257
}
58+
8359
// ------------------OBTAIN DATA FROM THE CURRENT FIBER NODE----------------
8460
// Destructure the current fiber node:
8561
const {
@@ -145,12 +121,12 @@ export default function createTree(currentFiberNode: Fiber): Tree {
145121
if (sibling) {
146122
_createTree(sibling, tree);
147123
}
148-
// -------------RETURN THE TREE OUTPUT & PASS TO FRONTEND FOR RENDERING-------
124+
// ---------RETURN THE TREE OUTPUT & PASS TO FRONTEND FOR RENDERING-------
149125
return tree;
150126
}
151127

152128
// --------------INITIALIZE OBJECT TO CONTAIN COMPONENT DATA---------------
153-
let newState: any | { hooksState?: any[] } = {};
129+
let newState: 'stateless' | object = 'stateless';
154130
let componentData: {
155131
actualDuration?: number;
156132
actualStartTime?: number;
@@ -170,7 +146,6 @@ export default function createTree(currentFiberNode: Fiber): Tree {
170146
props: {},
171147
context: {},
172148
};
173-
let isStatefulComponent = false;
174149

175150
// ---------------APPEND PROP DATA FROM REACT DEV TOOL----------------------
176151
// Check to see if the parent component has any props
@@ -239,8 +214,8 @@ export default function createTree(currentFiberNode: Fiber): Tree {
239214
// Save state information in componentData.
240215
componentData.state = stateNode.state;
241216
// Passess to front end
242-
newState = stateNode.state;
243-
isStatefulComponent = true;
217+
// TODO: Refactor this, this is currently being used for Tree & Diff tabs
218+
newState = componentData.state;
244219
}
245220

246221
// --------OBTAIN STATE & DISPATCH METHODS FROM FUNCTIONAL COMPONENT--------
@@ -261,31 +236,26 @@ export default function createTree(currentFiberNode: Fiber): Tree {
261236
const hooksStates = getHooksStateAndUpdateMethod(memoizedState);
262237
const hooksNames = getHooksNames(elementType.toString());
263238
// Intialize state & index:
264-
newState.hooksState = [];
239+
// newState.hooksState = [];
265240
componentData.hooksState = {};
266241
componentData.hooksIndex = [];
267242
hooksStates.forEach(({ state, component }, i) => {
268243
// Save component's state and dispatch() function to our record for future time-travel state changing. Add record index to snapshot so we can retrieve.
269244
componentData.hooksIndex.push(componentActionsRecord.saveNew(component));
270-
// Save state information in componentData.
271-
newState.hooksState.push({ [hooksNames[i].varName]: state });
272245
// Passess to front end
273246
componentData.hooksState[hooksNames[i].varName] = state;
274247
});
275-
isStatefulComponent = true;
248+
// Passess to front end
249+
// TODO: Refactor this, this is currently being used for Tree & Diff tabs
250+
newState = componentData.hooksState;
276251
} catch (err) {
277252
console.log('ERROR: Failed Element during JSX parsing', {
278-
componentName: elementType?.name,
253+
componentName,
279254
});
280255
}
281256
}
282257
}
283258

284-
// This grabs stateless components
285-
if (!isStatefulComponent) {
286-
newState = 'stateless';
287-
}
288-
289259
// -----------------ADD COMPONENT DATA TO THE OUTPUT TREE-------------------
290260

291261
/**

0 commit comments

Comments
 (0)