Skip to content

Commit 9f9eef3

Browse files
Merge pull request #10 from effector/fix-shallow-routing-races
Fix shallow routing races
2 parents 4065652 + 2cbf43c commit 9f9eef3

File tree

2 files changed

+66
-17
lines changed

2 files changed

+66
-17
lines changed

src/get-scope.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,32 @@ describe("getClientScope", () => {
8888
expect(clientScopeOne.getState(longUpFx.inFlight)).toEqual(0);
8989
expect(clientScopeOne.getState($specialData)).toEqual(getFixedDate());
9090
});
91+
test("shallow navigation to same page", async () => {
92+
const serverScope = fork();
93+
94+
await allSettled(up, { scope: serverScope });
95+
await allSettled(up, { scope: serverScope });
96+
await allSettled(up, { scope: serverScope });
97+
98+
const values = serialize(serverScope);
99+
100+
const clientScopeOne = getClientScope(values);
101+
102+
expect(clientScopeOne.getState($count)).toEqual(3);
103+
104+
await allSettled(up, { scope: clientScopeOne });
105+
106+
expect(clientScopeOne.getState($count)).toEqual(4);
107+
108+
// This imitates shallow navigation to same page, e.g. with different query params
109+
//
110+
// Next.js will reuse the same pageProps instance in this case
111+
// which will basically override current page state with initial one
112+
//
113+
// So we need to basically just ignore it, because
114+
// we already have the latest state in the client scope
115+
const clientScopeTwo = getClientScope(values);
116+
117+
expect(clientScopeTwo.getState($count)).toEqual(4);
118+
});
91119
});

src/get-scope.ts

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,31 @@ function getServerScope(values?: Values) {
1616
* This temporary solution on hacks allows us to solve the pain of library users when working with Next.js, as well as gather much more information to develop a better API.
1717
*/
1818
const _currentScope: Scope = fork();
19+
let prevValues: Values;
1920
/**
2021
* @private
2122
*
2223
* exported for tests only
2324
*/
2425
export function getClientScope(values?: Values) {
25-
if (!values) return _currentScope;
26+
if (
27+
!values ||
28+
/**
29+
* This is a hack to handle edge cases with shallow navigation
30+
*
31+
* In this case Next.js will basically re-use old pageProps,
32+
* but we already have latest state in the client scope
33+
*
34+
* So this update is just skipped
35+
*/
36+
values === prevValues
37+
)
38+
return _currentScope;
39+
40+
/**
41+
* Saving previous values to handle edge cases with shallow navigation
42+
*/
43+
prevValues = values;
2644

2745
HACK_injectValues(_currentScope, values);
2846
HACK_updateScopeRefs(_currentScope, values);
@@ -39,23 +57,26 @@ function HACK_updateScopeRefs(scope: Scope, values: Values) {
3957
// @ts-expect-error
4058
for (const id in scope.reg) {
4159
// @ts-expect-error
42-
const ref = scope.reg[id];
43-
if (!ref.meta || (!ref.meta?.named && ref.meta?.derived)) {
44-
/**
45-
* Force recalculation of derived values
46-
*/
60+
if (Object.hasOwnProperty.call(scope.reg, id)) {
4761
// @ts-expect-error
48-
delete scope.reg[id];
49-
} else {
50-
/**
51-
* Update non-derived values
52-
*/
53-
const sid = ref?.meta?.sid;
54-
if (sid && sid in values) {
55-
const serialize = ref?.meta?.serialize;
56-
const read =
57-
serialize && serialize !== "ingore" ? serialize?.read : null;
58-
ref.current = read ? read(values[sid]) : values[sid];
62+
const ref = scope.reg[id];
63+
if (!ref.meta || (!ref.meta?.named && ref.meta?.derived)) {
64+
/**
65+
* Force recalculation of derived values
66+
*/
67+
// @ts-expect-error
68+
delete scope.reg[id];
69+
} else {
70+
/**
71+
* Update non-derived values
72+
*/
73+
const sid = ref?.meta?.sid;
74+
if (sid && sid in values) {
75+
const serialize = ref?.meta?.serialize;
76+
const read =
77+
serialize && serialize !== "ingore" ? serialize?.read : null;
78+
ref.current = read ? read(values[sid]) : values[sid];
79+
}
5980
}
6081
}
6182
}

0 commit comments

Comments
 (0)