Skip to main content

Suspense

Navios DI React provides first-class support for React Suspense through the useSuspenseService hook.

Basic Usage

import { Suspense } from 'react'
import { useSuspenseService } from '@navios/di-react'

function UserProfile() {
const userService = useSuspenseService(UserService)
const [user, setUser] = useState(null)

useEffect(() => {
userService.getCurrentUser().then(setUser)
}, [userService])

return <div>{user?.name}</div>
}

function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<UserProfile />
</Suspense>
)
}

Error Boundaries

When using useSuspenseService, errors are thrown to the nearest error boundary:

import { ErrorBoundary } from 'react-error-boundary'
import { Suspense } from 'react'
import { useSuspenseService } from '@navios/di-react'

function ErrorFallback({ error }) {
return <div>Error: {error.message}</div>
}

function App() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Suspense fallback={<div>Loading...</div>}>
<UserProfile />
</Suspense>
</ErrorBoundary>
)
}

With Injection Tokens

import { useMemo } from 'react'
import { InjectionToken } from '@navios/di'
import { useSuspenseService } from '@navios/di-react'
import { z } from 'zod'

const UserSchema = z.object({ userId: z.string() })
const UserToken = InjectionToken.create<
{ userId: string; name: string },
typeof UserSchema
>('User', UserSchema)

function UserProfile({ userId }: { userId: string }) {
const args = useMemo(() => ({ userId }), [userId])
const user = useSuspenseService(UserToken, args)

return <div>{user?.name}</div>
}

Automatic Invalidation

useSuspenseService automatically subscribes to service invalidation events:

function UserProfile() {
const user = useSuspenseService(UserService)
const invalidateInstance = useInvalidateInstance()

const handleRefresh = async () => {
if (user) {
await invalidateInstance(user) // Component will re-render with fresh data
}
}

return (
<div>
<span>{user?.name}</span>
<button onClick={handleRefresh}>Refresh</button>
</div>
)
}

Best Practices

1. Always Use Error Boundaries

// ✅ Good: Error boundary with Suspense
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Suspense fallback={<Loading />}>
<Component />
</Suspense>
</ErrorBoundary>

2. Memoize Arguments

// ✅ Good: Memoized args
const args = useMemo(() => ({ userId }), [userId])
const user = useSuspenseService(UserToken, args)

// ❌ Bad: New object on every render
const user = useSuspenseService(UserToken, { userId })

Next Steps