Skip to content

Commit 31b9f90

Browse files
Merge pull request #4 from VNguyenCode/recoil
Added Recoil Functionality with new parsing algorithm in LinkFiber
2 parents 6380fa2 + fa138be commit 31b9f90

File tree

5 files changed

+134
-76
lines changed

5 files changed

+134
-76
lines changed

src/app/components/StateRoute.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ interface StateRouteProps {
3232
state?: string | object;
3333
stateSnaphot?: object;
3434
children?: any[];
35-
AtomsRelationship?: any[];
35+
AtomsComponents?: any;
36+
AtomsSelectors?: any;
37+
3638
};
3739
hierarchy: any;
3840
snapshots: [];
@@ -42,7 +44,7 @@ interface StateRouteProps {
4244
const StateRoute = (props: StateRouteProps) => {
4345
const { snapshot, hierarchy, snapshots, viewIndex } = props;
4446

45-
const isRecoil = snapshot.AtomsRelationship ? true : false;
47+
const isRecoil = snapshot.AtomsComponents ? true : false;
4648
const [noRenderData, setNoRenderData] = useState(false);
4749

4850
// component map zoom state
@@ -80,7 +82,7 @@ const StateRoute = (props: StateRouteProps) => {
8082
};
8183

8284
const renderAtomsRelationship = () => (
83-
<AtomsRelationship atomsRel={snapshot.AtomsRelationship} />
85+
<AtomsRelationship atomsRel={snapshot.AtomsComponents} />
8486
);
8587

8688
// the hierarchy gets set on the first click in the page

src/app/containers/StateContainer.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const StateContainer = (props:StateContainerProps): unknown => {
2828
snapshot, hierarchy, snapshots, viewIndex,
2929
} = props;
3030
const [Text, setText] = useState('State');
31+
32+
console.log(props)
33+
3134
return (
3235
<Router>
3336
<div className="state-container">

src/backend/linkFiber.ts

Lines changed: 89 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
import Tree from './tree';
2121
import componentActionsRecord from './masterState';
2222
import { throttle, getHooksNames } from './helpers';
23+
import { Console } from 'console';
24+
import AtomsRelationship from '../app/components/AtomsRelationship';
2325

2426
// Set global variables to use in exported module and helper functions
2527
declare global {
@@ -30,27 +32,29 @@ declare global {
3032
let fiberRoot = null;
3133
let doWork = true;
3234
const circularComponentTable = new Set();
33-
let allAtomsRelationship = [];
3435
let isRecoil = false;
36+
let allAtomsRelationship = [];
37+
let initialstart = false;
3538

3639
// Simple check for whether our target app uses Recoil
3740
if (window[`$recoilDebugStates`]) {
3841
isRecoil = true;
3942
}
4043

41-
function getRecoilState(): any {
42-
const RecoilSnapshotsLength = window[`$recoilDebugStates`].length;
43-
const lastRecoilSnapshot = window[`$recoilDebugStates`][RecoilSnapshotsLength - 1];
44-
const nodeToNodeSubs = lastRecoilSnapshot.nodeToNodeSubscriptions;
45-
const nodeToNodeSubsKeys = lastRecoilSnapshot.nodeToNodeSubscriptions.keys();
46-
nodeToNodeSubsKeys.forEach(
47-
node => {
48-
nodeToNodeSubs.get(node).forEach(
49-
nodeSubs => allAtomsRelationship.push([node, nodeSubs, 'atoms and selectors'])
50-
);
51-
}
52-
);
53-
}
44+
// function getRecoilState(): any {
45+
// const RecoilSnapshotsLength = window[`$recoilDebugStates`].length;
46+
// const lastRecoilSnapshot =
47+
// window[`$recoilDebugStates`][RecoilSnapshotsLength - 1];
48+
// const nodeToNodeSubs = lastRecoilSnapshot.nodeToNodeSubscriptions;
49+
// const nodeToNodeSubsKeys = lastRecoilSnapshot.nodeToNodeSubscriptions.keys();
50+
// nodeToNodeSubsKeys.forEach((node) => {
51+
// nodeToNodeSubs
52+
// .get(node)
53+
// .forEach((nodeSubs) =>
54+
// allAtomsRelationship.push([node, nodeSubs, 'atoms and selectors'])
55+
// );
56+
// });
57+
// }
5458

5559
/**
5660
* @method sendSnapshot
@@ -67,11 +71,14 @@ function sendSnapshot(snap: Snapshot, mode: Mode): void {
6771
snap.tree = new Tree('root', 'root');
6872
}
6973
const payload = snap.tree.cleanTreeCopy();
74+
7075
if (isRecoil) {
71-
getRecoilState();
72-
payload.AtomsRelationship = allAtomsRelationship;
76+
// getRecoilState();
77+
payload.AtomsComponents = atomsComponents;
78+
payload.AtomsSelectors = atomsSelectors;
7379
}
7480

81+
7582
window.postMessage(
7683
{
7784
action: 'recordSnap',
@@ -103,13 +110,16 @@ function updateSnapShotTree(snap: Snapshot, mode: Mode): void {
103110
* @param memoizedProps Property containing props on a stateful fctnl component's FiberNode object
104111
* @return An array of array of HookStateItem objects (state and component properties)
105112
*/
106-
function traverseRecoilHooks(memoizedState: any, memoizedProps: any): HookStates {
113+
function traverseRecoilHooks(
114+
memoizedState: any,
115+
memoizedProps: any
116+
): HookStates {
107117
const hooksStates: HookStates = [];
108118
while (memoizedState && memoizedState.queue) {
109119
if (
110-
memoizedState.memoizedState
111-
&& memoizedState.queue.lastRenderedReducer
112-
&& memoizedState.queue.lastRenderedReducer.name === 'basicStateReducer'
120+
memoizedState.memoizedState &&
121+
memoizedState.queue.lastRenderedReducer &&
122+
memoizedState.queue.lastRenderedReducer.name === 'basicStateReducer'
113123
) {
114124
if (Object.entries(memoizedProps).length !== 0) {
115125
hooksStates.push({
@@ -118,7 +128,8 @@ function traverseRecoilHooks(memoizedState: any, memoizedProps: any): HookStates
118128
});
119129
}
120130
}
121-
memoizedState = memoizedState.next !== memoizedState ? memoizedState.next : null;
131+
memoizedState =
132+
memoizedState.next !== memoizedState ? memoizedState.next : null;
122133
}
123134

124135
return hooksStates;
@@ -129,21 +140,20 @@ function traverseRecoilHooks(memoizedState: any, memoizedProps: any): HookStates
129140
* @param memoizedState memoizedState property on a stateful fctnl component's FiberNode object
130141
* @return An array of array of HookStateItem objects
131142
*
132-
* Helper function to traverse through memoizedState and inject instrumentation to update our state tree
143+
* Helper function to traverse through memoizedState and inject instrumentation to update our state tree
133144
* every time a hooks component changes state
134145
*/
135146
function traverseHooks(memoizedState: any): HookStates {
136147
const hooksStates: HookStates = [];
137148
while (memoizedState && memoizedState.queue) {
138-
if (
139-
memoizedState.memoizedState
140-
) {
149+
if (memoizedState.memoizedState) {
141150
hooksStates.push({
142151
component: memoizedState.queue,
143152
state: memoizedState.memoizedState,
144153
});
145154
}
146-
memoizedState = memoizedState.next !== memoizedState ? memoizedState.next : null;
155+
memoizedState =
156+
memoizedState.next !== memoizedState ? memoizedState.next : null;
147157
}
148158
return hooksStates;
149159
}
@@ -160,6 +170,9 @@ function traverseHooks(memoizedState: any): HookStates {
160170
* 3. Build a new state snapshot
161171
*/
162172
// This runs after every Fiber commit. It creates a new snapshot
173+
let atomsSelectors = {};
174+
let atomsComponents = {};
175+
163176
function createTree(
164177
currentFiber: Fiber,
165178
tree: Tree = new Tree('root', 'root'),
@@ -172,6 +185,7 @@ function createTree(
172185

173186
// These have the newest state. We update state and then
174187
// called updateSnapshotTree()
188+
175189
const {
176190
sibling,
177191
stateNode,
@@ -186,20 +200,49 @@ function createTree(
186200
treeBaseDuration,
187201
} = currentFiber;
188202

189-
if (elementType?.name && isRecoil) {
190-
let pointer = memoizedState;
191-
while (pointer !== null && pointer !== undefined && pointer.next !== null) {
192-
pointer = pointer.next;
193-
}
203+
//Checks Recoil Atom and Selector Relationships
204+
if (
205+
currentFiber.memoizedState &&
206+
currentFiber.memoizedState.next &&
207+
currentFiber.memoizedState.next.memoizedState &&
208+
currentFiber.memoizedState.next.memoizedState.deps &&
209+
isRecoil &&
210+
currentFiber.tag === 0 &&
211+
currentFiber.key === null //prevents capturing the same Fiber nodes but different key values that result from being changed
212+
) {
213+
let pointer = currentFiber.memoizedState.next;
214+
let componentName = currentFiber.elementType.name;
194215

195-
if (pointer?.memoizedState[1]?.[0].current) {
196-
const atomName = pointer.memoizedState[1]?.[0].current.keys().next().value;
197-
allAtomsRelationship.push([atomName, elementType?.name, 'atoms and components']);
216+
if (!atomsComponents[componentName]) {
217+
atomsComponents[componentName] = [];
218+
while (pointer !== null) {
219+
if (!Array.isArray(pointer.memoizedState)) {
220+
let atomName = pointer.memoizedState.deps[0]['key'];
221+
atomsComponents[componentName].push(atomName);
222+
}
223+
pointer = pointer.next;
224+
}
198225
}
199226

200-
if (pointer?.memoizedState[1]?.[0].key) {
201-
const atomName = pointer.memoizedState[1]?.[0].key;
202-
allAtomsRelationship.push([atomName, elementType?.name, 'atoms and components']);
227+
if (
228+
currentFiber.memoizedState.next.memoizedState.deps[1].current &&
229+
!initialstart
230+
) {
231+
let getState = currentFiber.memoizedState.next.memoizedState.deps[1].current.getState()
232+
.graphsByVersion;
233+
getState.entries().forEach((value) => {
234+
value[1].nodeDeps.entries().forEach((obj) => {
235+
if (!atomsSelectors[obj[0]]) {
236+
atomsSelectors[obj[0]] = [];
237+
}
238+
obj[1].values().forEach((selector) => {
239+
if (!atomsSelectors[obj[0]].includes(selector)) {
240+
atomsSelectors[obj[0]].push(selector);
241+
}
242+
});
243+
});
244+
});
245+
initialstart = true;
203246
}
204247
}
205248

@@ -235,17 +278,17 @@ function createTree(
235278

236279
// RECOIL HOOKS
237280
if (
238-
memoizedState
239-
&& (tag === 0 || tag === 1 || tag === 2 || tag === 10)
240-
&& isRecoil === true
281+
memoizedState &&
282+
(tag === 0 || tag === 1 || tag === 2 || tag === 10) &&
283+
isRecoil === true
241284
) {
242285
if (memoizedState.queue) {
243286
// Hooks states are stored as a linked list using memoizedState.next,
244287
// so we must traverse through the list and get the states.
245288
// We then store them along with the corresponding memoizedState.queue,
246289
// which includes the dispatch() function we use to change their state.
247290
const hooksStates = traverseRecoilHooks(memoizedState, memoizedProps);
248-
hooksStates.forEach(state => {
291+
hooksStates.forEach((state) => {
249292
hooksIndex = componentActionsRecord.saveNew(
250293
state.state,
251294
state.component
@@ -268,9 +311,9 @@ function createTree(
268311
// Check if node is a hooks useState function
269312
// REGULAR REACT HOOKS
270313
if (
271-
memoizedState
272-
&& (tag === 0 || tag === 1 || tag === 2 || tag === 10)
273-
&& isRecoil === false
314+
memoizedState &&
315+
(tag === 0 || tag === 1 || tag === 2 || tag === 10) &&
316+
isRecoil === false
274317
) {
275318
if (memoizedState.queue) {
276319
// Hooks states are stored as a linked list using memoizedState.next,
@@ -364,6 +407,7 @@ export default (snap: Snapshot, mode: Mode): (() => void) => {
364407
const devTools = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
365408
const reactInstance = devTools ? devTools.renderers.get(1) : null;
366409
fiberRoot = devTools.getFiberRoots(1).values().next().value;
410+
367411
const throttledUpdateSnapshot = throttle(() => updateSnapShotTree(snap, mode), 70);
368412
document.addEventListener('visibilitychange', onVisibilityChange);
369413
if (reactInstance && reactInstance.version) {
@@ -376,7 +420,7 @@ export default (snap: Snapshot, mode: Mode): (() => void) => {
376420
}
377421
return original(...args);
378422
};
379-
}(devTools.onCommitFiberRoot));
423+
})(devTools.onCommitFiberRoot);
380424
}
381425
throttledUpdateSnapshot();
382426
};

src/backend/tree.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ class Tree {
4545

4646
parent: Tree
4747

48-
AtomsRelationship: any;
48+
AtomsComponents: any;
49+
50+
AtomsSelectors: any;
4951

5052
constructor(state: string | {}, name = 'nameless', componentData: {} = {}) {
5153
this.state = state === 'root' ? 'root' : serializeState(state);

0 commit comments

Comments
 (0)