useDropper Hook
The useDropper hook provides access to the Dropper client, configuration, and theme from any component within a DropperProvider.
Basic Usage
import { useDropper } from '@dropper/react'
function MyComponent() {
const { client, config, theme } = useDropper()
// Use client to interact with API
const uploadFile = async (file: File) => {
const result = await client.uploadFile(file)
console.log('Uploaded:', result.url)
}
return <div>My Component</div>
}
Return Value
interface DropperContextValue {
client: DropperClient // Core API client
config: DropperConfig // Provider configuration
theme: ResolvedTheme // Resolved theme values
}
client
The DropperClient instance for API interactions:
const { client } = useDropper()
// Upload file
await client.uploadFile(file)
// List files
const files = await client.listFiles()
// Create folder
const folder = await client.createFolder({ name: 'My Folder' })
See Client Setup for full API reference.
config
The provider configuration:
const { config } = useDropper()
console.log(config.publishableKey) // "pk_dropper_test_xxx"
console.log(config.appId) // "app_123" (if provided)
console.log(config.baseUrl) // "https://api.dropper.com" (if provided)
theme
The resolved theme configuration:
const { theme } = useDropper()
console.log(theme.primary) // "#3b82f6"
console.log(theme.primary500) // Computed color shade
console.log(theme.radius) // "md"
useTheme Hook
For theme-only access, use the useTheme hook:
import { useTheme } from '@dropper/react'
function ThemedComponent() {
const theme = useTheme()
return (
<div style={{ color: theme.primary }}>
Themed content
</div>
)
}
Complete Examples
Custom File Upload
import { useDropper } from '@dropper/react'
import { useState } from 'react'
function CustomFileUpload() {
const { client } = useDropper()
const [uploading, setUploading] = useState(false)
const [progress, setProgress] = useState(0)
const handleUpload = async (file: File) => {
setUploading(true)
try {
const result = await client.uploadFile(file, {
onProgress: (p) => setProgress(p),
})
console.log('Upload successful:', result.url)
} catch (error) {
console.error('Upload failed:', error)
} finally {
setUploading(false)
setProgress(0)
}
}
return (
<div>
<input
type="file"
onChange={(e) => {
const file = e.target.files?.[0]
if (file) handleUpload(file)
}}
disabled={uploading}
/>
{uploading && <progress value={progress} max={100} />}
</div>
)
}
File List with Delete
import { useDropper } from '@dropper/react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
function FileList() {
const { client } = useDropper()
const queryClient = useQueryClient()
// Fetch files
const { data: files = [] } = useQuery({
queryKey: ['files'],
queryFn: () => client.listFiles().then(res => res.data),
})
// Delete mutation
const deleteMutation = useMutation({
mutationFn: (fileId: string) => client.deleteFile(fileId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['files'] })
},
})
return (
<div>
{files.map((file) => (
<div key={file.id}>
<span>{file.originalFilename}</span>
<button onClick={() => deleteMutation.mutate(file.id)}>
Delete
</button>
</div>
))}
</div>
)
}
Folder Management
import { useDropper } from '@dropper/react'
import { useState } from 'react'
function FolderManager() {
const { client } = useDropper()
const [folders, setFolders] = useState([])
const [loading, setLoading] = useState(false)
const loadFolders = async () => {
setLoading(true)
try {
const response = await client.listFolders()
setFolders(response.folders)
} catch (error) {
console.error('Failed to load folders:', error)
} finally {
setLoading(false)
}
}
const createFolder = async (name: string) => {
try {
await client.createFolder({ name })
await loadFolders() // Refresh list
} catch (error) {
console.error('Failed to create folder:', error)
}
}
return (
<div>
<button onClick={loadFolders} disabled={loading}>
Load Folders
</button>
<button onClick={() => createFolder('New Folder')}>
Create Folder
</button>
<ul>
{folders.map((folder) => (
<li key={folder.id}>{folder.name}</li>
))}
</ul>
</div>
)
}
Themed Button
import { useTheme } from '@dropper/react'
function ThemedButton({ children, onClick }) {
const theme = useTheme()
return (
<button
onClick={onClick}
style={{
backgroundColor: theme.primary,
color: 'white',
borderRadius: theme.radius === 'sm' ? '4px' :
theme.radius === 'md' ? '8px' : '12px',
padding: '8px 16px',
border: 'none',
cursor: 'pointer',
}}
>
{children}
</button>
)
}
Access Configuration
import { useDropper } from '@dropper/react'
function ConfigDisplay() {
const { config } = useDropper()
return (
<div>
<h3>Dropper Configuration</h3>
<dl>
<dt>API Key:</dt>
<dd>{config.publishableKey.substring(0, 20)}...</dd>
{config.appId && (
<>
<dt>App ID:</dt>
<dd>{config.appId}</dd>
</>
)}
{config.baseUrl && (
<>
<dt>Base URL:</dt>
<dd>{config.baseUrl}</dd>
</>
)}
</dl>
</div>
)
}
Error Handling
The hook will throw an error if used outside DropperProvider:
import { useDropper } from '@dropper/react'
function MyComponent() {
try {
const { client } = useDropper()
// Use client
} catch (error) {
// Error: useDropper must be used within a DropperProvider
return <div>Please wrap this component with DropperProvider</div>
}
}
TypeScript Support
The hook is fully typed:
import { useDropper, useTheme } from '@dropper/react'
import type { DropperClient } from '@dropper/core'
import type { ResolvedTheme } from '@dropper/react'
function TypedComponent() {
const { client, config, theme } = useDropper()
// client is typed as DropperClient
const uploadFile = async (file: File) => {
const result = await client.uploadFile(file)
// result is typed as FileResponseDto
}
// theme is typed as ResolvedTheme
const primaryColor: string = theme.primary
return <div />
}
Best Practices
1. Use at Component Level
Call the hook in components, not at module level:
// Good
function MyComponent() {
const { client } = useDropper()
return <div />
}
// Bad
const { client } = useDropper() // Error: Outside component
function MyComponent() {
return <div />
}
2. Destructure What You Need
Only destructure the values you need:
// Good: Only need client
const { client } = useDropper()
// Avoid: Destructuring unused values
const { client, config, theme } = useDropper()
3. Combine with React Query
Use with React Query hooks for data fetching:
import { useDropper } from '@dropper/react'
import { useQuery } from '@tanstack/react-query'
function FileList() {
const { client } = useDropper()
const { data: files } = useQuery({
queryKey: ['files'],
queryFn: () => client.listFiles().then(res => res.data),
})
return <div>{/* Render files */}</div>
}
4. Use useTheme for Styling
Use useTheme when you only need theme values:
import { useTheme } from '@dropper/react'
function StyledComponent() {
const theme = useTheme()
return (
<div style={{ color: theme.primary }}>
Themed content
</div>
)
}
Common Patterns
Conditional Rendering
function ConditionalComponent() {
const { config } = useDropper()
if (config.appId === 'demo') {
return <DemoMode />
}
return <ProductionMode />
}
Custom Hooks
Build custom hooks on top of useDropper:
import { useDropper } from '@dropper/react'
import { useQuery } from '@tanstack/react-query'
function useFiles(folderId?: string) {
const { client } = useDropper()
return useQuery({
queryKey: ['files', folderId],
queryFn: () => client.listFiles({ folderId }).then(res => res.data),
})
}
// Usage
function MyComponent() {
const { data: files } = useFiles('folder_123')
return <div>{/* Render files */}</div>
}
Memoization
Memoize expensive operations:
import { useDropper } from '@dropper/react'
import { useMemo } from 'react'
function ExpensiveComponent() {
const { theme } = useDropper()
const computedStyles = useMemo(() => ({
primary: theme.primary,
secondary: theme.secondary,
// Expensive computation
}), [theme])
return <div style={computedStyles}>Content</div>
}
Next Steps
- Provider Setup - Configure the provider
- File Upload Component - Upload files
- File List Component - Display files
- Folder Browser - Navigate folders