diff --git a/packages/react/src/Deferred.ts b/packages/react/src/Deferred.ts
index 4d2cb4fe8..4be3369aa 100644
--- a/packages/react/src/Deferred.ts
+++ b/packages/react/src/Deferred.ts
@@ -1,4 +1,5 @@
-import { ReactElement, useEffect, useState } from 'react'
+import { ReactElement, useEffect, useMemo, useState } from 'react'
+import { router } from '.'
import usePage from './usePage'
interface DeferredProps {
@@ -14,7 +15,22 @@ const Deferred = ({ children, data, fallback }: DeferredProps) => {
const [loaded, setLoaded] = useState(false)
const pageProps = usePage().props
- const keys = Array.isArray(data) ? data : [data]
+ const keys = useMemo(() => (Array.isArray(data) ? data : [data]), [data])
+
+ useEffect(() => {
+ const removeListener = router.on('start', (e) => {
+ if (
+ (e.detail.visit.only.length === 0 && e.detail.visit.except.length === 0) ||
+ e.detail.visit.only.find((key) => keys.includes(key))
+ ) {
+ setLoaded(false)
+ }
+ })
+
+ return () => {
+ removeListener()
+ }
+ }, [])
useEffect(() => {
setLoaded(keys.every((key) => pageProps[key] !== undefined))
diff --git a/packages/react/test-app/Pages/DeferredProps/WithPartialReload.jsx b/packages/react/test-app/Pages/DeferredProps/WithPartialReload.jsx
new file mode 100644
index 000000000..396ad5a2d
--- /dev/null
+++ b/packages/react/test-app/Pages/DeferredProps/WithPartialReload.jsx
@@ -0,0 +1,33 @@
+import { Deferred, router, usePage } from '@inertiajs/react'
+
+const WithPartialReload = ({ withOnly, withExcept }) => {
+ const handleTriggerPartialReload = () => {
+ router.reload({
+ only: withOnly,
+ except: withExcept,
+ })
+ }
+
+ return (
+
+ Loading...}>
+
+
+
+
+ )
+}
+
+const DeferredUsers = () => {
+ const props = usePage().props
+
+ return (
+
+ {props.users.map((user) => (
+ {user.name}
+ ))}
+
+ )
+}
+
+export default WithPartialReload
diff --git a/tests/app/server.js b/tests/app/server.js
index ab0f47f26..4ac1d2b1f 100644
--- a/tests/app/server.js
+++ b/tests/app/server.js
@@ -293,6 +293,52 @@ app.get('/deferred-props/page-1', (req, res) => {
)
})
+app.get('/deferred-props/with-partial-reload/:mode', (req, res) => {
+ if (!req.headers['x-inertia-partial-data']) {
+ return inertia.render(req, res, {
+ component: 'DeferredProps/WithPartialReload',
+ deferredProps: {
+ default: ['users'],
+ },
+ props: {
+ withOnly: (() => {
+ if (req.params.mode === 'only') {
+ return ['users']
+ }
+
+ if (req.params.mode === 'only-other') {
+ return ['other']
+ }
+
+ return []
+ })(),
+ withExcept: (() => {
+ if (req.params.mode === 'except') {
+ return ['users']
+ }
+
+ if (req.params.mode === 'except-other') {
+ return ['other']
+ }
+
+ return []
+ })(),
+ },
+ })
+ }
+
+ setTimeout(
+ () =>
+ inertia.render(req, res, {
+ component: 'DeferredProps/WithPartialReload',
+ props: {
+ users: req.headers['x-inertia-partial-data']?.includes('users') ? [{ id: 1, name: 'John Doe' }] : undefined,
+ },
+ }),
+ 500,
+ )
+})
+
app.get('/deferred-props/page-2', (req, res) => {
if (!req.headers['x-inertia-partial-data']) {
return inertia.render(req, res, {
diff --git a/tests/deferred-props.spec.ts b/tests/deferred-props.spec.ts
index 2b41f2f79..7437de6bf 100644
--- a/tests/deferred-props.spec.ts
+++ b/tests/deferred-props.spec.ts
@@ -88,3 +88,54 @@ test('props will re-defer if a link is clicked to go to the same page again', as
await expect(page.getByText('foo value')).toBeVisible()
await expect(page.getByText('bar value')).toBeVisible()
})
+
+const shoulReload = ['only']
+
+shoulReload.forEach((type) => {
+ test(`it will handle partial reloads properly when deferred is being reloaded (${type})`, async ({ page }) => {
+ test.skip(process.env.PACKAGE !== 'react', 'React only test')
+
+ await page.goto(`/deferred-props/with-partial-reload/${type}`)
+
+ await expect(page.getByText('Loading...')).toBeVisible()
+
+ await page.waitForResponse(page.url())
+
+ await expect(page.getByText('Loading...')).not.toBeVisible()
+ await expect(page.getByText('John Doe')).toBeVisible()
+
+ const responsePromise = page.waitForResponse(page.url())
+
+ await page.getByRole('button', { exact: true, name: 'Trigger a partial reload' }).click()
+ await expect(page.getByText('Loading...')).toBeVisible()
+
+ await responsePromise
+
+ await expect(page.getByText('John Doe')).toBeVisible()
+ })
+})
+
+const noReload = ['except', 'only-other', 'none', 'except-other']
+
+noReload.forEach((type) => {
+ test(`it will handle partial reloads properly when deferred is not reloaded (${type})`, async ({ page }) => {
+ test.skip(process.env.PACKAGE !== 'react', 'React only test')
+
+ await page.goto(`/deferred-props/with-partial-reload/${type}`)
+
+ await expect(page.getByText('Loading...')).toBeVisible()
+
+ await page.waitForResponse(page.url())
+
+ await expect(page.getByText('Loading...')).not.toBeVisible()
+ await expect(page.getByText('John Doe')).toBeVisible()
+
+ const responsePromise = page.waitForResponse(page.url())
+ await page.getByRole('button', { exact: true, name: 'Trigger a partial reload' }).click()
+ await expect(page.getByText('Loading...')).not.toBeVisible()
+
+ await responsePromise
+
+ await expect(page.getByText('John Doe')).toBeVisible()
+ })
+})