Skip to content

Commit 3364fcd

Browse files
committed
Merge branch 'master' into precognition-useform
2 parents c8980fc + 0e7e658 commit 3364fcd

File tree

15 files changed

+659
-58
lines changed

15 files changed

+659
-58
lines changed

packages/react/src/WhenVisible.ts

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ReloadOptions, router } from '@inertiajs/core'
22
import { createElement, ReactNode, useCallback, useEffect, useRef, useState } from 'react'
3+
import usePage from './usePage'
34

45
interface WhenVisibleProps {
56
children: ReactNode | (() => ReactNode)
@@ -17,9 +18,25 @@ const WhenVisible = ({ children, data, params, buffer, as, always, fallback }: W
1718
fallback = fallback ?? null
1819

1920
const [loaded, setLoaded] = useState(false)
20-
const hasFetched = useRef<boolean>(false)
2121
const fetching = useRef<boolean>(false)
2222
const ref = useRef<HTMLDivElement>(null)
23+
const observer = useRef<IntersectionObserver | null>(null)
24+
25+
const page = usePage()
26+
27+
useEffect(() => {
28+
if (Array.isArray(data)) {
29+
// For arrays, reset loaded if any prop becomes undefined
30+
if (data.some((key) => page.props[key] === undefined)) {
31+
setLoaded(false)
32+
}
33+
} else if (data) {
34+
// For single prop, reset loaded if prop becomes undefined
35+
if (page.props[data] === undefined) {
36+
setLoaded(false)
37+
}
38+
}
39+
}, [data, ...(Array.isArray(data) ? data.map((key) => page.props[key]) : [page.props[data!]])])
2340

2441
const getReloadParams = useCallback<() => Partial<ReloadOptions>>(() => {
2542
if (data) {
@@ -35,26 +52,23 @@ const WhenVisible = ({ children, data, params, buffer, as, always, fallback }: W
3552
return params
3653
}, [params, data])
3754

38-
useEffect(() => {
39-
if (!ref.current) {
40-
return
41-
}
55+
const registerObserver = () => {
56+
observer.current?.disconnect()
4257

43-
const observer = new IntersectionObserver(
58+
observer.current = new IntersectionObserver(
4459
(entries) => {
4560
if (!entries[0].isIntersecting) {
4661
return
4762
}
4863

49-
if (!always && hasFetched.current) {
50-
observer.disconnect()
64+
if (fetching.current) {
65+
return
5166
}
5267

53-
if (fetching.current) {
68+
if (!always && loaded) {
5469
return
5570
}
5671

57-
hasFetched.current = true
5872
fetching.current = true
5973

6074
const reloadParams = getReloadParams()
@@ -71,7 +85,7 @@ const WhenVisible = ({ children, data, params, buffer, as, always, fallback }: W
7185
reloadParams.onFinish?.(e)
7286

7387
if (!always) {
74-
observer.disconnect()
88+
observer.current?.disconnect()
7589
}
7690
},
7791
})
@@ -81,12 +95,20 @@ const WhenVisible = ({ children, data, params, buffer, as, always, fallback }: W
8195
},
8296
)
8397

84-
observer.observe(ref.current)
98+
observer.current.observe(ref.current!)
99+
}
100+
101+
useEffect(() => {
102+
if (!ref.current) {
103+
return
104+
}
105+
106+
registerObserver()
85107

86108
return () => {
87-
observer.disconnect()
109+
observer.current?.disconnect()
88110
}
89-
}, [ref, getReloadParams, buffer])
111+
}, [loaded, ref, getReloadParams, buffer])
90112

91113
const resolveChildren = () => (typeof children === 'function' ? children() : children)
92114
const resolveFallback = () => (typeof fallback === 'function' ? fallback() : fallback)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Deferred, router, usePage } from '@inertiajs/react'
2+
3+
const FooTimestamp = () => {
4+
const { foo } = usePage<{ foo?: { timestamp: string } }>().props
5+
6+
return <div id="foo-timestamp">{foo?.timestamp}</div>
7+
}
8+
9+
const BarTimestamp = () => {
10+
const { bar } = usePage<{ bar?: { timestamp: string } }>().props
11+
12+
return <div id="bar-timestamp">{bar?.timestamp}</div>
13+
}
14+
15+
const PartialReloads = () => {
16+
const reloadOnlyFoo = () => {
17+
router.reload({
18+
only: ['foo'],
19+
})
20+
}
21+
22+
const reloadOnlyBar = () => {
23+
router.reload({
24+
only: ['bar'],
25+
})
26+
}
27+
28+
const reloadBoth = () => {
29+
router.reload({
30+
only: ['foo', 'bar'],
31+
})
32+
}
33+
34+
return (
35+
<>
36+
<Deferred data="foo" fallback={<div>Loading foo...</div>}>
37+
<FooTimestamp />
38+
</Deferred>
39+
40+
<Deferred data="bar" fallback={<div>Loading bar...</div>}>
41+
<BarTimestamp />
42+
</Deferred>
43+
44+
<button onClick={reloadOnlyFoo}>Reload foo only</button>
45+
<button onClick={reloadOnlyBar}>Reload bar only</button>
46+
<button onClick={reloadBoth}>Reload both</button>
47+
</>
48+
)
49+
}
50+
51+
export default PartialReloads
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { router, WhenVisible } from '@inertiajs/react'
2+
3+
interface Props {
4+
firstData?: {
5+
text: string
6+
}
7+
secondData?: {
8+
text: string
9+
}
10+
}
11+
12+
export default ({ firstData, secondData }: Props) => {
13+
const handleReload = () => {
14+
router.reload()
15+
}
16+
17+
return (
18+
<div>
19+
<h1>WhenVisible + Array Props + Reload</h1>
20+
21+
<button onClick={handleReload}>Reload Page</button>
22+
23+
<div style={{ marginTop: '2000px', padding: '20px', border: '1px solid #ccc' }}>
24+
<WhenVisible data={['firstData', 'secondData']} fallback={<p>Loading array data...</p>}>
25+
<div>
26+
<p>{firstData?.text}</p>
27+
<p>{secondData?.text}</p>
28+
</div>
29+
</WhenVisible>
30+
</div>
31+
</div>
32+
)
33+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { router, WhenVisible } from '@inertiajs/react'
2+
3+
interface Props {
4+
lazyData?: {
5+
text: string
6+
}
7+
}
8+
9+
export default ({ lazyData }: Props) => {
10+
const handleReload = () => {
11+
router.reload()
12+
}
13+
14+
return (
15+
<div>
16+
<h1>WhenVisible + Reload</h1>
17+
18+
<button onClick={handleReload}>Reload Page</button>
19+
20+
<div style={{ marginTop: '2000px', padding: '20px', border: '1px solid #ccc' }}>
21+
<WhenVisible data="lazyData" fallback={<p>Loading lazy data...</p>}>
22+
{lazyData?.text}
23+
</WhenVisible>
24+
</div>
25+
</div>
26+
)
27+
}

packages/svelte/src/components/WhenVisible.svelte

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script lang="ts">
22
import { router, type ReloadOptions } from '@inertiajs/core'
3-
import { onDestroy, onMount } from 'svelte'
3+
import { onDestroy } from 'svelte'
4+
import { usePage } from '../page'
45
56
export let data: string | string[] = ''
67
export let params: ReloadOptions = {}
@@ -13,22 +14,38 @@
1314
let el: HTMLElement
1415
let observer: IntersectionObserver | null = null
1516
16-
onMount(() => {
17-
if (!el) {
18-
return
17+
const page = usePage()
18+
19+
// Watch for page prop changes and reset loaded state when data becomes undefined
20+
$: {
21+
if (Array.isArray(data)) {
22+
// For arrays, reset loaded if any prop becomes undefined
23+
if (data.some((key) => $page.props[key] === undefined)) {
24+
loaded = false
25+
}
26+
} else if ($page.props[data as string] === undefined) {
27+
loaded = false
1928
}
29+
}
30+
31+
$: if (el) {
32+
registerObserver()
33+
}
34+
35+
function registerObserver() {
36+
observer?.disconnect()
2037
2138
observer = new IntersectionObserver(
2239
(entries) => {
2340
if (!entries[0].isIntersecting) {
2441
return
2542
}
2643
27-
if (!always) {
28-
observer?.disconnect()
44+
if (fetching) {
45+
return
2946
}
3047
31-
if (fetching) {
48+
if (!always && loaded) {
3249
return
3350
}
3451
@@ -46,6 +63,10 @@
4663
loaded = true
4764
fetching = false
4865
reloadParams.onFinish?.(event)
66+
67+
if (!always) {
68+
observer?.disconnect()
69+
}
4970
},
5071
})
5172
},
@@ -55,7 +76,7 @@
5576
)
5677
5778
observer.observe(el)
58-
})
79+
}
5980
6081
onDestroy(() => {
6182
observer?.disconnect()
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script lang="ts">
2+
import { Deferred, router } from '@inertiajs/svelte'
3+
4+
export let foo: { timestamp: string } | undefined
5+
export let bar: { timestamp: string } | undefined
6+
7+
const reloadOnlyFoo = () => {
8+
router.reload({
9+
only: ['foo'],
10+
})
11+
}
12+
13+
const reloadOnlyBar = () => {
14+
router.reload({
15+
only: ['bar'],
16+
})
17+
}
18+
19+
const reloadBoth = () => {
20+
router.reload({
21+
only: ['foo', 'bar'],
22+
})
23+
}
24+
</script>
25+
26+
<Deferred data="foo">
27+
<svelte:fragment slot="fallback">
28+
<div>Loading foo...</div>
29+
</svelte:fragment>
30+
<div id="foo-timestamp">{foo?.timestamp}</div>
31+
</Deferred>
32+
33+
<Deferred data="bar">
34+
<svelte:fragment slot="fallback">
35+
<div>Loading bar...</div>
36+
</svelte:fragment>
37+
<div id="bar-timestamp">{bar?.timestamp}</div>
38+
</Deferred>
39+
40+
<button on:click={reloadOnlyFoo}>Reload foo only</button>
41+
<button on:click={reloadOnlyBar}>Reload bar only</button>
42+
<button on:click={reloadBoth}>Reload both</button>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<script lang="ts">
2+
import { router, WhenVisible } from '@inertiajs/svelte'
3+
4+
export let firstData:
5+
| {
6+
text: string
7+
}
8+
| undefined = undefined
9+
10+
export let secondData:
11+
| {
12+
text: string
13+
}
14+
| undefined = undefined
15+
16+
const handleReload = () => {
17+
router.reload()
18+
}
19+
</script>
20+
21+
<div>
22+
<h1>WhenVisible + Array Props + Reload</h1>
23+
24+
<button on:click={handleReload}>Reload Page</button>
25+
26+
<div style="margin-top: 2000px; padding: 20px; border: 1px solid #ccc">
27+
<WhenVisible data={['firstData', 'secondData']}>
28+
<svelte:fragment slot="fallback">
29+
<p>Loading array data...</p>
30+
</svelte:fragment>
31+
32+
<div>
33+
<p>{firstData?.text}</p>
34+
<p>{secondData?.text}</p>
35+
</div>
36+
</WhenVisible>
37+
</div>
38+
</div>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<script lang="ts">
2+
import { router, WhenVisible } from '@inertiajs/svelte'
3+
4+
export let lazyData:
5+
| {
6+
text: string
7+
}
8+
| undefined = undefined
9+
10+
const handleReload = () => {
11+
router.reload()
12+
}
13+
</script>
14+
15+
<div>
16+
<h1>WhenVisible + Reload</h1>
17+
18+
<button on:click={handleReload}>Reload Page</button>
19+
20+
<div style="margin-top: 2000px; padding: 20px; border: 1px solid #ccc">
21+
<WhenVisible data="lazyData">
22+
<svelte:fragment slot="fallback">
23+
<p>Loading lazy data...</p>
24+
</svelte:fragment>
25+
26+
{lazyData?.text}
27+
</WhenVisible>
28+
</div>
29+
</div>

0 commit comments

Comments
 (0)