Skip to content

Commit ad25cf1

Browse files
Merge pull request #878 from grahammendick/manual-refetch
2 parents 6c15ab1 + 74f55fc commit ad25cf1

File tree

6 files changed

+33
-46
lines changed

6 files changed

+33
-46
lines changed

NavigationReact/src/AsyncStateNavigator.ts

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,42 +18,6 @@ class AsyncStateNavigator extends StateNavigator {
1818
this.offNavigate = stateNavigator.offNavigate.bind(stateNavigator);
1919
}
2020

21-
refresh(navigationData?: any, historyAction?: 'add' | 'replace' | 'none') {
22-
const refreshData = { ...this.stateContext.state.defaults, ...navigationData };
23-
const refreshKeys = Object.keys(refreshData);
24-
let equal = refreshKeys.length === Object.keys(this.stateContext.data).length;
25-
for (let i = 0; i < refreshKeys.length && equal; i++) {
26-
const key = refreshKeys[i];
27-
equal = equal && AsyncStateNavigator.areEqual(refreshData[key], this.stateContext.data[key]);
28-
}
29-
if (!equal)
30-
super.refresh(navigationData, historyAction);
31-
else {
32-
const { oldState, state, data, asyncData } = this.stateContext;
33-
const startTransition = React.startTransition || ((transition) => transition());
34-
startTransition(() => {
35-
this.navigationHandler.setState({ context: { ignoreCache: true, oldState, state, data, asyncData, stateNavigator: this } });
36-
});
37-
}
38-
}
39-
40-
private static areEqual(val: any, currentVal: any): boolean {
41-
if (currentVal == null)
42-
return val == null || val === '';
43-
const valType = Object.prototype.toString.call(val);
44-
if (valType !== Object.prototype.toString.call(currentVal))
45-
return false;
46-
if (valType === '[object Array]') {
47-
let active = val.length === currentVal.length;
48-
for(let i = 0; active && i < val.length; i++) {
49-
active = this.areEqual(val[i], currentVal[i]);
50-
}
51-
return active;
52-
} else {
53-
return isNaN(val) ? val === currentVal : +val === +currentVal;
54-
}
55-
}
56-
5721
navigateLink(url: string, historyAction: 'add' | 'replace' | 'none' = 'add', history = false,
5822
suspendNavigation?: (stateContext: StateContext, resumeNavigation: () => void) => void,
5923
currentContext = this.stateContext) {

NavigationReact/src/NavigationHandler.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use client'
2-
import React, { Component } from 'react';
2+
import React, { Component, startTransition } from 'react';
33
import { StateNavigator, State } from 'navigation';
44
import AsyncStateNavigator from './AsyncStateNavigator.js';
55
import NavigationContext from './NavigationContext.js';
6+
import SceneRSCContext from './SceneRSCContext.js';
67
type NavigationHandlerState = { context: { ignoreCache?: boolean, oldState: State, state: State, data: any, asyncData: any, stateNavigator: AsyncStateNavigator } };
78

89
class NavigationHandler extends Component<{ stateNavigator: StateNavigator, children: any }, NavigationHandlerState> {
@@ -13,6 +14,7 @@ class NavigationHandler extends Component<{ stateNavigator: StateNavigator, chil
1314
const asyncNavigator = new AsyncStateNavigator(this, stateNavigator, stateNavigator.stateContext);
1415
this.state = { context: { oldState, state, data, asyncData, stateNavigator: asyncNavigator } };
1516
this.onNavigate = this.onNavigate.bind(this);
17+
this.refetch = this.refetch.bind(this);
1618
}
1719

1820
componentDidMount() {
@@ -33,10 +35,18 @@ class NavigationHandler extends Component<{ stateNavigator: StateNavigator, chil
3335
}
3436
}
3537

38+
refetch() {
39+
startTransition(() => {
40+
this.setState({ context: { ...this.state.context, ignoreCache: true } });
41+
});
42+
}
43+
3644
render() {
3745
return (
3846
<NavigationContext.Provider value={this.state.context}>
39-
{this.props.children}
47+
<SceneRSCContext.Provider value={this.refetch}>
48+
{this.props.children}
49+
</SceneRSCContext.Provider>
4050
</NavigationContext.Provider>
4151
);
4252
}

NavigationReact/src/RSCContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import { createContext } from 'react';
22
import { StateContext } from 'navigation';
33

4-
export default createContext<{fetching: boolean, setRefetch: (refetch?: string[] | ((stateContext: StateContext) => boolean)) => void}>({fetching: false, setRefetch: null});
4+
export default createContext<{fetching: boolean, setRefetch: (refetch?: string[] | ((stateContext: StateContext) => boolean)) => void, refetcher: (scene?: boolean) => void}>({fetching: false, setRefetch: null, refetcher: null});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createContext } from 'react';
2+
3+
export default createContext<() => void>(null);

NavigationReact/src/SceneRSCView.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use client'
2-
import React, { use, useContext, useEffect, useRef, useMemo } from 'react';
2+
import React, { use, useContext, useEffect, useRef, useMemo, useOptimistic, startTransition } from 'react';
33
import { SceneViewProps } from './Props.js';
44
import useNavigationEvent from './useNavigationEvent.js';
55
import BundlerContext from './BundlerContext.js';
@@ -17,15 +17,16 @@ const SceneRSCView = ({active, name, refetch: serverRefetch, errorFallback, chil
1717
const {state, oldState, data, stateNavigator: {stateContext, historyManager}} = navigationEvent;
1818
const {crumbs, nextCrumb: {crumblessUrl: url}, oldUrl, oldData, history, historyAction} = stateContext;
1919
const {deserialize} = useContext(BundlerContext);
20-
const {fetching: ancestorFetching} = useContext(RSCContext);
20+
const rscContext = useContext(RSCContext);
2121
const sceneViewKey = name || (typeof active === 'string' ? active : active[0]);
2222
const getShow = (stateKey: string) => (
2323
active != null && state && (
2424
typeof active === 'string' ? stateKey === active : active.indexOf(stateKey) !== -1
2525
)
2626
);
2727
const show = getShow(state.key);
28-
const ignoreCache = !!navigationEvent['ignoreCache'];
28+
const refetcherState = {ancestorFetching: rscContext.fetching, ignoreCache: !!navigationEvent['ignoreCache']};
29+
const [{ancestorFetching, ignoreCache}, refetcher] = useOptimistic<any, void>(refetcherState, () => ({ancestorFetching: false, ignoreCache: true}));
2930
const cachedHistory = !ignoreCache && history && !!historyCache[url]?.[sceneViewKey];
3031
if (!rscCache.get(navigationEvent)) rscCache.set(navigationEvent, {});
3132
const cachedSceneViews = rscCache.get(navigationEvent);
@@ -59,8 +60,8 @@ const SceneRSCView = ({active, name, refetch: serverRefetch, errorFallback, chil
5960
const sceneView = (() => {
6061
if (!show) return null;
6162
if (cachedHistory) return historyCache[url][sceneViewKey];
63+
if (cachedSceneViews[sceneViewKey]) return cachedSceneViews[sceneViewKey];
6264
if (firstScene || ancestorFetching) return children;
63-
if (fetching) return cachedSceneViews[sceneViewKey];
6465
return renderedSceneView.current.sceneView;
6566
})();
6667
useEffect(() => {
@@ -94,8 +95,14 @@ const SceneRSCView = ({active, name, refetch: serverRefetch, errorFallback, chil
9495
});
9596
const rscContextVal = useMemo(() => ({
9697
fetching: ancestorFetching || fetching,
97-
setRefetch: (refetch: any) => refetchRef.current = refetch !== undefined ? refetch : serverRefetch
98-
}), [ancestorFetching || fetching]);
98+
setRefetch: (refetch: any) => refetchRef.current = refetch !== undefined ? refetch : serverRefetch,
99+
refetcher: () => {
100+
startTransition(() => {
101+
delete cachedSceneViews[sceneViewKey];
102+
refetcher();
103+
})
104+
}
105+
}), [ancestorFetching || fetching, cachedSceneViews, refetcher]);
99106
return (
100107
<ErrorBoundary errorFallback={errorFallback}>
101108
<RSCContext.Provider value={rscContextVal}>

NavigationReact/src/useRefetch.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { useContext } from 'react';
22
import { StateContext } from 'navigation';
33
import RSCContext from './RSCContext.js';
4+
import SceneRSCContext from './SceneRSCContext.js';
45

56
const useRefetch = (refetch?: string[] | ((stateContext: StateContext) => boolean) | null) => {
6-
const {setRefetch} = useContext(RSCContext);
7+
const {setRefetch, refetcher} = useContext(RSCContext);
8+
const sceneRefetcher = useContext(SceneRSCContext);
79
setRefetch(refetch);
10+
return (scene = false) => !scene ? refetcher() : sceneRefetcher();
811
}
912
export default useRefetch;

0 commit comments

Comments
 (0)