Skip to content

Commit 5982a1c

Browse files
authored
Merge pull request #2 from oslabs-beta/robby/feature/react-router/backend
Robby/feature/react router/backend
2 parents 640b860 + a258cd0 commit 5982a1c

File tree

5 files changed

+173
-42
lines changed

5 files changed

+173
-42
lines changed

src/backend/linkFiber.ts

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
import Tree from './tree';
2929
// passes the data down to its components ?
3030
import componentActionsRecord from './masterState';
31+
import routes from './routes';
3132

3233
// throttle returns a function that can be called any number of times (possibly in quick succession) but will only invoke the callback at most once every x ms
3334
// getHooksNames - helper function to grab the getters/setters from `elementType`
@@ -93,6 +94,7 @@ function sendSnapshot(snap: Snapshot, mode: Mode): void {
9394
snap.tree = new Tree('root', 'root');
9495
}
9596
const payload = snap.tree.cleanTreeCopy();
97+
payload.route = routes.addRoute(window.location.href);
9698
// if it's Recoil - run different actions
9799
if (isRecoil) {
98100
// getRecoilState()
@@ -132,6 +134,7 @@ function updateSnapShotTree(snap: Snapshot, mode: Mode): void {
132134
// Clears circular component table
133135
circularComponentTable.clear();
134136
// creates snapshot that is a tree based on properties in fiberRoot object
137+
componentActionsRecord.clear();
135138
snap.tree = createTree(current);
136139
}
137140
// sends the updated tree back
@@ -355,7 +358,7 @@ function createTree(
355358
// time-travel state changing. Add record index to snapshot so we can retrieve.
356359
componentData.index = componentActionsRecord.saveNew(
357360
stateNode.state,
358-
stateNode
361+
stateNode,
359362
);
360363
newState = stateNode.state;
361364
componentFound = true;
@@ -365,32 +368,32 @@ function createTree(
365368
atomArray.push(memoizedProps);
366369

367370
// RECOIL HOOKS
368-
if (
369-
memoizedState
370-
&& (tag === 0 || tag === 1 || tag === 2 || tag === 10)
371-
) {
372-
if (memoizedState.queue) {
373-
// Hooks states are stored as a linked list using memoizedState.next,
374-
// so we must traverse through the list and get the states.
375-
// We then store them along with the corresponding memoizedState.queue,
376-
// which includes the dispatch() function we use to change their state.
377-
const hooksStates = traverseRecoilHooks(memoizedState, memoizedProps);
378-
hooksStates.forEach((state, i) => {
379-
hooksIndex = componentActionsRecord.saveNew(
380-
state.state,
381-
state.component
382-
);
383-
componentData.hooksIndex = hooksIndex;
384-
if (!newState) {
385-
newState = { hooksState: [] };
386-
} else if (!newState.hooksState) {
387-
newState.hooksState = [];
388-
}
389-
newState.hooksState.push({ [i]: state.state });
390-
componentFound = true;
391-
});
392-
}
393-
}
371+
// if (
372+
// memoizedState
373+
// && (tag === 0 || tag === 1 || tag === 2 || tag === 10)
374+
// ) {
375+
// if (memoizedState.queue) {
376+
// // Hooks states are stored as a linked list using memoizedState.next,
377+
// // so we must traverse through the list and get the states.
378+
// // We then store them along with the corresponding memoizedState.queue,
379+
// // which includes the dispatch() function we use to change their state.
380+
// const hooksStates = traverseRecoilHooks(memoizedState, memoizedProps);
381+
// hooksStates.forEach((state, i) => {
382+
// hooksIndex = componentActionsRecord.saveNew(
383+
// state.state,
384+
// state.component
385+
// );
386+
// componentData.hooksIndex = hooksIndex;
387+
// if (!newState) {
388+
// newState = { hooksState: [] };
389+
// } else if (!newState.hooksState) {
390+
// newState.hooksState = [];
391+
// }
392+
// newState.hooksState.push({ [i]: state.state });
393+
// componentFound = true;
394+
// });
395+
// }
396+
// }
394397

395398
// Check if node is a hooks useState function
396399
// REGULAR REACT HOOKS
@@ -410,7 +413,7 @@ function createTree(
410413
hooksStates.forEach((state, i) => {
411414
hooksIndex = componentActionsRecord.saveNew(
412415
state.state,
413-
state.component
416+
state.component,
414417
);
415418
componentData.hooksIndex = hooksIndex;
416419
if (!newState) {

src/backend/masterState.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,34 @@ import {
1515
// Information on these components include ComponentData as well as state
1616
// For class components, there will be one "component" for each snapshot
1717
// For functional components that utilize Hooks, there will be one "component" for each setter/getter every time we have a new snapshot
18-
const componentActionsRecord: HookStates = [];
18+
let componentActionsRecord: HookStates = [];
1919
let index = 0;
2020

2121
export default {
22-
//adds new component to ComponentActionsRecord
22+
clear: () => {
23+
componentActionsRecord = [];
24+
index = 0;
25+
},
26+
// adds new component to ComponentActionsRecord
2327
saveNew: (state, component): number => {
2428
componentActionsRecord[index] = { state, component };
2529
index++;
30+
2631
return index - 1;
2732
},
28-
getRecordByIndex: (inputIndex: number): HookStateItem => componentActionsRecord[inputIndex],
29-
//this is used for class components - inputIndex will always be a fixed number (coming in timeJump.ts)
30-
getComponentByIndex: (inputIndex: number): any => (componentActionsRecord[inputIndex]
31-
? componentActionsRecord[inputIndex].component
32-
: undefined),
33-
//this is used for react hooks - hooks will be passed in as an array from timeJump.ts
33+
getRecordByIndex: (inputIndex: number): HookStateItem =>
34+
componentActionsRecord[inputIndex],
35+
// this is used for class components - inputIndex will always be a fixed number (coming in timeJump.ts)
36+
getComponentByIndex: (inputIndex: number): any =>
37+
componentActionsRecord[inputIndex]
38+
? componentActionsRecord[inputIndex].component
39+
: undefined,
40+
// this is used for react hooks - hooks will be passed in as an array from timeJump.ts
3441
getComponentByIndexHooks: (inputIndex: Array<number> = []): any => {
3542
const multiDispatch = [];
3643
for (let i = 0; i < inputIndex.length; i++) {
37-
if (componentActionsRecord[inputIndex[i]]) multiDispatch.push(componentActionsRecord[inputIndex[i]].component);
44+
if (componentActionsRecord[inputIndex[i]])
45+
multiDispatch.push(componentActionsRecord[inputIndex[i]].component);
3846
}
3947
return multiDispatch;
4048
},

src/backend/routes.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/* eslint-disable max-classes-per-file */
2+
/* eslint-disable max-len */
3+
4+
/**
5+
* @class Route instances are created by the addRoute method on Routes. A Route instance has two properties: the url of the route and a unique id.
6+
*/
7+
class Route {
8+
url: string;
9+
10+
id: number;
11+
12+
constructor(url: string, id: number) {
13+
this.url = url;
14+
this.id = id;
15+
}
16+
}
17+
18+
/**
19+
* @class An instance of this class is the default export from routes.ts. It includes the logic that allows Reactime to work with target applications built with React Router. The addRoute method is invoked in linkFiber.ts within the sendSnapshot function. The navigate method is invoked in timeJump.ts immediately before invoking jump.
20+
*/
21+
22+
class Routes {
23+
/**
24+
* @property A stack of visited routes that matches the browser history stack.
25+
*/
26+
values: Route[] = [new Route(null, null)];
27+
28+
/**
29+
* @property Used to assign unique ids to routes in the values stack in case the same route is added to the stack more than once.
30+
*/
31+
id = 0;
32+
33+
/**
34+
* @property The index of the current route in the values stack.
35+
*/
36+
current: number | null = 0;
37+
38+
/**
39+
* @method addRoute
40+
* @param url - A url string.
41+
* @returns Either the current route if the user has not navigated away from it or a new instance of a route constructed from the url.
42+
*
43+
* Ensures that the values stack always matches the browser history stack.
44+
*/
45+
46+
addRoute(url: string): Route {
47+
let route: Route = this.values[this.current];
48+
49+
if (this.values[this.current].url !== url) {
50+
if (this.current !== this.values.length - 1) {
51+
this.rebuildHistory(url);
52+
}
53+
54+
route = new Route(url, (this.id += 1));
55+
this.values.push(route);
56+
57+
this.current = this.values.length - 1;
58+
}
59+
60+
return route;
61+
}
62+
63+
/**
64+
* @method rebuildHistory
65+
* @param url - A url string.
66+
*
67+
* Rebuilds the browser history stack using the copy of the stack maintained in the values stack. https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState, https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
68+
*/
69+
private rebuildHistory(url: string): void {
70+
window.history.replaceState('', '', this.values[this.current + 1].url);
71+
72+
for (let i = this.current + 2; i < this.values.length; i += 1) {
73+
window.history.pushState('', '', this.values[i].url);
74+
}
75+
76+
window.history.pushState('', '', url);
77+
}
78+
79+
/**
80+
* @method navigate
81+
* @param route - The target route in the values stack that is being navigated to.
82+
* @returns A boolean indicating whether or not a new route was navigated to.
83+
*
84+
* Invokes history.go passing in the delta between the current route and the target route. https://developer.mozilla.org/en-US/docs/Web/API/History/go
85+
*/
86+
navigate(route: Route): boolean {
87+
let targetIndex: number | null = null;
88+
89+
for (let i = 0; i < this.values.length; i += 1) {
90+
if (this.values[i].url === route.url && this.values[i].id === route.id) {
91+
targetIndex = i;
92+
}
93+
}
94+
95+
const delta: number = targetIndex - this.current;
96+
97+
this.current += delta;
98+
99+
if (delta !== 0) {
100+
window.history.go(delta);
101+
return true;
102+
}
103+
return false;
104+
}
105+
}
106+
107+
export default new Routes();

src/backend/timeJump.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Console } from 'console';
2+
import routes from './routes';
23

34
/* eslint-disable @typescript-eslint/no-unused-vars */
45
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
@@ -85,9 +86,19 @@ export default (origin, mode) => {
8586
// * Setting mode disables setState from posting messages to window
8687
mode.jumping = true;
8788
if (firstCall) circularComponentTable.clear();
88-
jump(target);
89-
setTimeout(() => {
90-
mode.jumping = false;
91-
}, 100);
89+
const navigating: boolean = routes.navigate(target.route);
90+
if (navigating) {
91+
addEventListener('popstate', event => {
92+
jump(target);
93+
document.body.onmouseover = () => {
94+
mode.jumping = false;
95+
};
96+
});
97+
} else {
98+
jump(target);
99+
document.body.onmouseover = () => {
100+
mode.jumping = false;
101+
};
102+
}
92103
};
93-
};
104+
};

src/backend/tree.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class Tree {
6161

6262
recoilDomNode: any;
6363

64+
route: {};
65+
6466
// Duplicate names: add a unique number ID
6567
// Create an object 'componentNames' to store each component name as a key and it's frequency of use as its value
6668
// When a new component is made on the tree, check if the new component's name already exists in 'componentNames' (possibly with the .hasOwnProperty method)

0 commit comments

Comments
 (0)