Skip to content

Commit

Permalink
Merge pull request open-source-labs#4 from VNguyenCode/recoil
Browse files Browse the repository at this point in the history
Added Recoil Functionality with new parsing algorithm in LinkFiber
  • Loading branch information
Theqwertypusher authored Oct 7, 2020
2 parents 6380fa2 + fa138be commit 31b9f90
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 76 deletions.
8 changes: 5 additions & 3 deletions src/app/components/StateRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ interface StateRouteProps {
state?: string | object;
stateSnaphot?: object;
children?: any[];
AtomsRelationship?: any[];
AtomsComponents?: any;
AtomsSelectors?: any;

};
hierarchy: any;
snapshots: [];
Expand All @@ -42,7 +44,7 @@ interface StateRouteProps {
const StateRoute = (props: StateRouteProps) => {
const { snapshot, hierarchy, snapshots, viewIndex } = props;

const isRecoil = snapshot.AtomsRelationship ? true : false;
const isRecoil = snapshot.AtomsComponents ? true : false;
const [noRenderData, setNoRenderData] = useState(false);

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

const renderAtomsRelationship = () => (
<AtomsRelationship atomsRel={snapshot.AtomsRelationship} />
<AtomsRelationship atomsRel={snapshot.AtomsComponents} />
);

// the hierarchy gets set on the first click in the page
Expand Down
3 changes: 3 additions & 0 deletions src/app/containers/StateContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ const StateContainer = (props:StateContainerProps): unknown => {
snapshot, hierarchy, snapshots, viewIndex,
} = props;
const [Text, setText] = useState('State');

console.log(props)

return (
<Router>
<div className="state-container">
Expand Down
134 changes: 89 additions & 45 deletions src/backend/linkFiber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
import Tree from './tree';
import componentActionsRecord from './masterState';
import { throttle, getHooksNames } from './helpers';
import { Console } from 'console';
import AtomsRelationship from '../app/components/AtomsRelationship';

// Set global variables to use in exported module and helper functions
declare global {
Expand All @@ -30,27 +32,29 @@ declare global {
let fiberRoot = null;
let doWork = true;
const circularComponentTable = new Set();
let allAtomsRelationship = [];
let isRecoil = false;
let allAtomsRelationship = [];
let initialstart = false;

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

function getRecoilState(): any {
const RecoilSnapshotsLength = window[`$recoilDebugStates`].length;
const lastRecoilSnapshot = window[`$recoilDebugStates`][RecoilSnapshotsLength - 1];
const nodeToNodeSubs = lastRecoilSnapshot.nodeToNodeSubscriptions;
const nodeToNodeSubsKeys = lastRecoilSnapshot.nodeToNodeSubscriptions.keys();
nodeToNodeSubsKeys.forEach(
node => {
nodeToNodeSubs.get(node).forEach(
nodeSubs => allAtomsRelationship.push([node, nodeSubs, 'atoms and selectors'])
);
}
);
}
// function getRecoilState(): any {
// const RecoilSnapshotsLength = window[`$recoilDebugStates`].length;
// const lastRecoilSnapshot =
// window[`$recoilDebugStates`][RecoilSnapshotsLength - 1];
// const nodeToNodeSubs = lastRecoilSnapshot.nodeToNodeSubscriptions;
// const nodeToNodeSubsKeys = lastRecoilSnapshot.nodeToNodeSubscriptions.keys();
// nodeToNodeSubsKeys.forEach((node) => {
// nodeToNodeSubs
// .get(node)
// .forEach((nodeSubs) =>
// allAtomsRelationship.push([node, nodeSubs, 'atoms and selectors'])
// );
// });
// }

/**
* @method sendSnapshot
Expand All @@ -67,11 +71,14 @@ function sendSnapshot(snap: Snapshot, mode: Mode): void {
snap.tree = new Tree('root', 'root');
}
const payload = snap.tree.cleanTreeCopy();

if (isRecoil) {
getRecoilState();
payload.AtomsRelationship = allAtomsRelationship;
// getRecoilState();
payload.AtomsComponents = atomsComponents;
payload.AtomsSelectors = atomsSelectors;
}


window.postMessage(
{
action: 'recordSnap',
Expand Down Expand Up @@ -103,13 +110,16 @@ function updateSnapShotTree(snap: Snapshot, mode: Mode): void {
* @param memoizedProps Property containing props on a stateful fctnl component's FiberNode object
* @return An array of array of HookStateItem objects (state and component properties)
*/
function traverseRecoilHooks(memoizedState: any, memoizedProps: any): HookStates {
function traverseRecoilHooks(
memoizedState: any,
memoizedProps: any
): HookStates {
const hooksStates: HookStates = [];
while (memoizedState && memoizedState.queue) {
if (
memoizedState.memoizedState
&& memoizedState.queue.lastRenderedReducer
&& memoizedState.queue.lastRenderedReducer.name === 'basicStateReducer'
memoizedState.memoizedState &&
memoizedState.queue.lastRenderedReducer &&
memoizedState.queue.lastRenderedReducer.name === 'basicStateReducer'
) {
if (Object.entries(memoizedProps).length !== 0) {
hooksStates.push({
Expand All @@ -118,7 +128,8 @@ function traverseRecoilHooks(memoizedState: any, memoizedProps: any): HookStates
});
}
}
memoizedState = memoizedState.next !== memoizedState ? memoizedState.next : null;
memoizedState =
memoizedState.next !== memoizedState ? memoizedState.next : null;
}

return hooksStates;
Expand All @@ -129,21 +140,20 @@ function traverseRecoilHooks(memoizedState: any, memoizedProps: any): HookStates
* @param memoizedState memoizedState property on a stateful fctnl component's FiberNode object
* @return An array of array of HookStateItem objects
*
* Helper function to traverse through memoizedState and inject instrumentation to update our state tree
* Helper function to traverse through memoizedState and inject instrumentation to update our state tree
* every time a hooks component changes state
*/
function traverseHooks(memoizedState: any): HookStates {
const hooksStates: HookStates = [];
while (memoizedState && memoizedState.queue) {
if (
memoizedState.memoizedState
) {
if (memoizedState.memoizedState) {
hooksStates.push({
component: memoizedState.queue,
state: memoizedState.memoizedState,
});
}
memoizedState = memoizedState.next !== memoizedState ? memoizedState.next : null;
memoizedState =
memoizedState.next !== memoizedState ? memoizedState.next : null;
}
return hooksStates;
}
Expand All @@ -160,6 +170,9 @@ function traverseHooks(memoizedState: any): HookStates {
* 3. Build a new state snapshot
*/
// This runs after every Fiber commit. It creates a new snapshot
let atomsSelectors = {};
let atomsComponents = {};

function createTree(
currentFiber: Fiber,
tree: Tree = new Tree('root', 'root'),
Expand All @@ -172,6 +185,7 @@ function createTree(

// These have the newest state. We update state and then
// called updateSnapshotTree()

const {
sibling,
stateNode,
Expand All @@ -186,20 +200,49 @@ function createTree(
treeBaseDuration,
} = currentFiber;

if (elementType?.name && isRecoil) {
let pointer = memoizedState;
while (pointer !== null && pointer !== undefined && pointer.next !== null) {
pointer = pointer.next;
}
//Checks Recoil Atom and Selector Relationships
if (
currentFiber.memoizedState &&
currentFiber.memoizedState.next &&
currentFiber.memoizedState.next.memoizedState &&
currentFiber.memoizedState.next.memoizedState.deps &&
isRecoil &&
currentFiber.tag === 0 &&
currentFiber.key === null //prevents capturing the same Fiber nodes but different key values that result from being changed
) {
let pointer = currentFiber.memoizedState.next;
let componentName = currentFiber.elementType.name;

if (pointer?.memoizedState[1]?.[0].current) {
const atomName = pointer.memoizedState[1]?.[0].current.keys().next().value;
allAtomsRelationship.push([atomName, elementType?.name, 'atoms and components']);
if (!atomsComponents[componentName]) {
atomsComponents[componentName] = [];
while (pointer !== null) {
if (!Array.isArray(pointer.memoizedState)) {
let atomName = pointer.memoizedState.deps[0]['key'];
atomsComponents[componentName].push(atomName);
}
pointer = pointer.next;
}
}

if (pointer?.memoizedState[1]?.[0].key) {
const atomName = pointer.memoizedState[1]?.[0].key;
allAtomsRelationship.push([atomName, elementType?.name, 'atoms and components']);
if (
currentFiber.memoizedState.next.memoizedState.deps[1].current &&
!initialstart
) {
let getState = currentFiber.memoizedState.next.memoizedState.deps[1].current.getState()
.graphsByVersion;
getState.entries().forEach((value) => {
value[1].nodeDeps.entries().forEach((obj) => {
if (!atomsSelectors[obj[0]]) {
atomsSelectors[obj[0]] = [];
}
obj[1].values().forEach((selector) => {
if (!atomsSelectors[obj[0]].includes(selector)) {
atomsSelectors[obj[0]].push(selector);
}
});
});
});
initialstart = true;
}
}

Expand Down Expand Up @@ -235,17 +278,17 @@ function createTree(

// RECOIL HOOKS
if (
memoizedState
&& (tag === 0 || tag === 1 || tag === 2 || tag === 10)
&& isRecoil === true
memoizedState &&
(tag === 0 || tag === 1 || tag === 2 || tag === 10) &&
isRecoil === true
) {
if (memoizedState.queue) {
// Hooks states are stored as a linked list using memoizedState.next,
// so we must traverse through the list and get the states.
// We then store them along with the corresponding memoizedState.queue,
// which includes the dispatch() function we use to change their state.
const hooksStates = traverseRecoilHooks(memoizedState, memoizedProps);
hooksStates.forEach(state => {
hooksStates.forEach((state) => {
hooksIndex = componentActionsRecord.saveNew(
state.state,
state.component
Expand All @@ -268,9 +311,9 @@ function createTree(
// Check if node is a hooks useState function
// REGULAR REACT HOOKS
if (
memoizedState
&& (tag === 0 || tag === 1 || tag === 2 || tag === 10)
&& isRecoil === false
memoizedState &&
(tag === 0 || tag === 1 || tag === 2 || tag === 10) &&
isRecoil === false
) {
if (memoizedState.queue) {
// Hooks states are stored as a linked list using memoizedState.next,
Expand Down Expand Up @@ -364,6 +407,7 @@ export default (snap: Snapshot, mode: Mode): (() => void) => {
const devTools = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
const reactInstance = devTools ? devTools.renderers.get(1) : null;
fiberRoot = devTools.getFiberRoots(1).values().next().value;

const throttledUpdateSnapshot = throttle(() => updateSnapShotTree(snap, mode), 70);
document.addEventListener('visibilitychange', onVisibilityChange);
if (reactInstance && reactInstance.version) {
Expand All @@ -376,7 +420,7 @@ export default (snap: Snapshot, mode: Mode): (() => void) => {
}
return original(...args);
};
}(devTools.onCommitFiberRoot));
})(devTools.onCommitFiberRoot);
}
throttledUpdateSnapshot();
};
Expand Down
4 changes: 3 additions & 1 deletion src/backend/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ class Tree {

parent: Tree

AtomsRelationship: any;
AtomsComponents: any;

AtomsSelectors: any;

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

0 comments on commit 31b9f90

Please sign in to comment.