Best Practices
This guide covers best practices for using Navios DI React effectively in your React applications.
Container Management
1. Keep Container Reference Stable
// ✅ Good: Stable container reference
const container = new Container()
function App() {
return (
<ContainerProvider container={container}>
<YourApp />
</ContainerProvider>
)
}
// ❌ Bad: Creates new container on every render
function App() {
return (
<ContainerProvider container={new Container()}>
<YourApp />
</ContainerProvider>
)
}
2. Use useMemo for Dynamic Containers
// ✅ Good: Memoized container
const container = useMemo(() => new Container(), [])
// ❌ Bad: New container on every render
const container = new Container()
Hooks Usage
1. Memoize Arguments
Always memoize arguments passed to hooks that accept them:
// ✅ Good: Memoized args
const args = useMemo(() => ({ userId }), [userId])
const { data } = useService(UserToken, args)
// ❌ Bad: Creates new object on every render
const { data } = useService(UserToken, { userId })
2. Use Suspense for Cleaner Code
// ✅ Good: Use Suspense
function UserProfile() {
const service = useSuspenseService(UserService)
return <div>{service.data}</div>
}
<Suspense fallback={<Loading />}>
<UserProfile />
</Suspense>
3. Use Error Boundaries with Suspense
// ✅ Good: Error boundary with Suspense
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Suspense fallback={<Loading />}>
<Component />
</Suspense>
</ErrorBoundary>
Scope Management
1. Use ScopeProvider for Isolation
// ✅ Good: Isolated scopes for table rows
{rows.map((row) => (
<ScopeProvider key={row.id} scopeId={row.id}>
<TableRow />
</ScopeProvider>
))}
2. Provide Meaningful Scope IDs
// ✅ Good: Descriptive scope IDs
<ScopeProvider scopeId={`user-${userId}`}>
// ❌ Avoid: Generic scope IDs
<ScopeProvider scopeId="scope">
Invalidation
1. Invalidate After Mutations
// ✅ Good: Invalidate after mutations
const { data: items } = useService(ItemService)
const invalidateInstance = useInvalidateInstance()
const handleCreate = async () => {
await createItem(data)
if (items) {
await invalidateInstance(items) // Refresh the list
}
}
2. Use refetch for Simple Refreshes
// ✅ Good: Use refetch for component-local refresh
const { data, refetch } = useService(UserService)
const handleRefresh = () => {
refetch() // Re-fetches the service
}
Performance
1. Avoid Unnecessary Re-renders
// ✅ Good: Memoize to avoid re-renders
const args = useMemo(() => ({ userId }), [userId])
const { data } = useService(UserToken, args)
// ❌ Bad: New object causes re-renders
const { data } = useService(UserToken, { userId })
2. Use Optional Services for Feature Flags
// ✅ Good: Optional service for feature flags
const { data: feature, isNotFound } = useOptionalService(FeatureService)
if (isNotFound) return null // Feature not enabled
Common Pitfalls
1. Service Re-fetches on Every Render
Problem: Service re-fetches because arguments are not memoized.
Solution: Always memoize arguments:
// ✅ Good: Memoized args
const args = useMemo(() => ({ userId }), [userId])
const { data } = useService(UserToken, args)
2. Container Created on Every Render
Problem: New container created on every render.
Solution: Keep container reference stable:
// ✅ Good: Stable container
const container = new Container()