Skip to main content

Hooks

Navios DI React provides several hooks for accessing services in your React components.

useService

Fetch a service with loading/error states. Automatically re-fetches when the service is invalidated.

import { useService } from '@navios/di-react'

function UserProfile() {
const { data, isLoading, isError, error, refetch } = useService(UserService)

if (isLoading) return <div>Loading...</div>
if (isError) return <div>Error: {error?.message}</div>

return (
<div>
<h1>{data?.name}</h1>
<button onClick={refetch}>Refresh</button>
</div>
)
}

With Injection Tokens and Arguments

import { useMemo } from 'react'
import { InjectionToken } from '@navios/di'
import { useService } 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 }) {
// Important: Memoize args to avoid unnecessary re-fetches
const args = useMemo(() => ({ userId }), [userId])
const { data: user, isLoading } = useService(UserToken, args)

if (isLoading) return <div>Loading...</div>

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

useSuspenseService

Use with React Suspense for a cleaner loading experience. Also subscribes to service invalidation.

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>
)
}

useOptionalService

Load a service that may not be registered. Unlike useService, this hook does NOT throw an error if the service is not registered.

import { useOptionalService } from '@navios/di-react'

function Analytics() {
const {
data: analytics,
isNotFound,
isLoading,
} = useOptionalService(AnalyticsService)

if (isLoading) return null
if (isNotFound) {
// Analytics service not configured, skip tracking
return null
}

return <AnalyticsTracker service={analytics} />
}

useInvalidateInstance

Get a function to invalidate a service instance. When called, this will destroy the current service instance and trigger re-fetch in all components using useService/useSuspenseService for that service.

import { useService, useInvalidateInstance } from '@navios/di-react'

function UserProfile() {
const { data: user } = useService(UserService)
const invalidateInstance = useInvalidateInstance()

const handleRefresh = async () => {
if (user) {
await invalidateInstance(user) // All components using UserService will re-fetch
}
}

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

With Injection Tokens

import { useMemo } from 'react'
import { useService, useInvalidateInstance } from '@navios/di-react'

function UserProfile({ userId }: { userId: string }) {
const args = useMemo(() => ({ userId }), [userId])
const { data: user } = useService(UserToken, args)
const invalidateInstance = useInvalidateInstance()

const handleRefresh = async () => {
if (user) {
await invalidateInstance(user)
}
}

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

useContainer

Access the container directly. Automatically returns the ScopedContainer if inside a ScopeProvider, otherwise returns the root Container.

import { useContainer } from '@navios/di-react'

function MyComponent() {
const container = useContainer()

const handleClick = async () => {
const service = await container.get(MyService)
service.doSomething()
}

return <button onClick={handleClick}>Do Something</button>
}

useRootContainer

Get the root container regardless of whether you're inside a ScopeProvider.

import { useRootContainer } from '@navios/di-react'

function MyComponent() {
const rootContainer = useRootContainer()

const createNewScope = () => {
const scopedContainer = rootContainer.beginRequest('new-scope')
// Use scopedContainer...
}

return <button onClick={createNewScope}>Create Scope</button>
}

Next Steps

  • Learn about providers for container and scope management
  • Explore invalidation for service invalidation patterns
  • See suspense for React Suspense integration