mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
Reorganize documentation: move all docs to /docs subdirectories
Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
392
docs/api/COMPLETE_HOOK_LIBRARY.md
Normal file
392
docs/api/COMPLETE_HOOK_LIBRARY.md
Normal file
@@ -0,0 +1,392 @@
|
||||
# Complete Hook Library Reference
|
||||
|
||||
## Overview
|
||||
|
||||
The CodeForge hook library provides comprehensive, reusable React hooks organized by domain. All hooks are under 150 LOC, fully typed, and designed for composition.
|
||||
|
||||
## Organization
|
||||
|
||||
```
|
||||
src/hooks/
|
||||
├── data/ # Data management hooks
|
||||
├── ui/ # UI state management hooks
|
||||
├── forms/ # Form and validation hooks
|
||||
├── feature-ideas/ # Feature-specific hooks
|
||||
├── core/ # Core utility hooks
|
||||
├── ai/ # AI integration hooks
|
||||
└── orchestration/ # JSON orchestration hooks
|
||||
```
|
||||
|
||||
## Data Management Hooks
|
||||
|
||||
### `useArray<T>(key, defaultValue)`
|
||||
|
||||
Enhanced array management with persistence.
|
||||
|
||||
**Features:**
|
||||
- Add/remove items
|
||||
- Update items with predicates
|
||||
- Find and filter operations
|
||||
- Auto-persists to KV store
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useArray } from '@/hooks/data'
|
||||
|
||||
const { items, add, remove, update, find, count } = useArray<Todo>('todos', [])
|
||||
|
||||
add({ id: '1', text: 'Learn hooks', done: false })
|
||||
update(
|
||||
(todo) => todo.id === '1',
|
||||
(todo) => ({ ...todo, done: true })
|
||||
)
|
||||
remove((todo) => todo.done)
|
||||
```
|
||||
|
||||
### `useCRUD<T>(items, setItems)`
|
||||
|
||||
Complete CRUD operations for entity management.
|
||||
|
||||
**Features:**
|
||||
- Create, read, update, delete operations
|
||||
- Selection management
|
||||
- Duplicate functionality
|
||||
- Type-safe operations
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useCRUD } from '@/hooks/data'
|
||||
import { useKV } from '@github/spark/hooks'
|
||||
|
||||
const [tasks, setTasks] = useKV('tasks', [])
|
||||
const {
|
||||
create,
|
||||
update,
|
||||
remove,
|
||||
selected,
|
||||
setSelectedId
|
||||
} = useCRUD(tasks, setTasks)
|
||||
|
||||
create({ id: '1', name: 'New Task', status: 'pending' })
|
||||
update('1', { status: 'completed' })
|
||||
setSelectedId('1')
|
||||
```
|
||||
|
||||
### `useSearch<T>(items, searchKeys, debounceMs)`
|
||||
|
||||
Debounced search across multiple fields.
|
||||
|
||||
**Features:**
|
||||
- Multi-field search
|
||||
- Debounced input
|
||||
- Type-safe key selection
|
||||
- Performance optimized
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useSearch } from '@/hooks/data'
|
||||
|
||||
const { query, setQuery, results, isSearching } = useSearch(
|
||||
users,
|
||||
['name', 'email', 'role'],
|
||||
300
|
||||
)
|
||||
|
||||
// In your component
|
||||
<Input value={query} onChange={(e) => setQuery(e.target.value)} />
|
||||
{results.map(user => <UserCard key={user.id} user={user} />)}
|
||||
```
|
||||
|
||||
### `useDebounce<T>(value, delay)`
|
||||
|
||||
Debounce any value changes.
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useDebounce } from '@/hooks/data'
|
||||
|
||||
const [input, setInput] = useState('')
|
||||
const debouncedInput = useDebounce(input, 500)
|
||||
|
||||
useEffect(() => {
|
||||
// API call with debounced value
|
||||
fetchResults(debouncedInput)
|
||||
}, [debouncedInput])
|
||||
```
|
||||
|
||||
### `useSort<T>(items, defaultKey)`
|
||||
|
||||
Sort items by any key with direction toggle.
|
||||
|
||||
**Features:**
|
||||
- String, number, and generic comparison
|
||||
- Toggle ascending/descending
|
||||
- Type-safe sort keys
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useSort } from '@/hooks/data'
|
||||
|
||||
const { sortedItems, sortKey, sortDirection, toggleSort } = useSort(
|
||||
products,
|
||||
'name'
|
||||
)
|
||||
|
||||
<Button onClick={() => toggleSort('price')}>
|
||||
Sort by Price {sortKey === 'price' && (sortDirection === 'asc' ? '↑' : '↓')}
|
||||
</Button>
|
||||
```
|
||||
|
||||
### `usePagination<T>(items, initialPageSize)`
|
||||
|
||||
Client-side pagination with full controls.
|
||||
|
||||
**Features:**
|
||||
- Page navigation
|
||||
- Dynamic page size
|
||||
- Has next/prev indicators
|
||||
- Total page calculation
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { usePagination } from '@/hooks/data'
|
||||
|
||||
const {
|
||||
items,
|
||||
page,
|
||||
totalPages,
|
||||
nextPage,
|
||||
prevPage,
|
||||
goToPage,
|
||||
hasNext,
|
||||
hasPrev
|
||||
} = usePagination(allItems, 20)
|
||||
|
||||
<Button onClick={prevPage} disabled={!hasPrev}>Previous</Button>
|
||||
<span>Page {page} of {totalPages}</span>
|
||||
<Button onClick={nextPage} disabled={!hasNext}>Next</Button>
|
||||
```
|
||||
|
||||
## UI State Hooks
|
||||
|
||||
### `useDialog(initialOpen)`
|
||||
|
||||
Simple dialog/modal state management.
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useDialog } from '@/hooks/ui'
|
||||
|
||||
const { isOpen, open, close, toggle } = useDialog()
|
||||
|
||||
<Button onClick={open}>Open Dialog</Button>
|
||||
<Dialog open={isOpen} onOpenChange={(open) => open ? open() : close()}>
|
||||
...
|
||||
</Dialog>
|
||||
```
|
||||
|
||||
### `useTabs<T>(defaultTab)`
|
||||
|
||||
Type-safe tab navigation.
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useTabs } from '@/hooks/ui'
|
||||
|
||||
const { activeTab, switchTab, isActive } = useTabs<'overview' | 'details' | 'settings'>('overview')
|
||||
|
||||
<Tabs value={activeTab} onValueChange={switchTab}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||
<TabsTrigger value="details">Details</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
```
|
||||
|
||||
### `useSelection<T>()`
|
||||
|
||||
Multi-select state management.
|
||||
|
||||
**Features:**
|
||||
- Select/deselect individual items
|
||||
- Select all/deselect all
|
||||
- Toggle selection
|
||||
- Selection count
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useSelection } from '@/hooks/ui'
|
||||
|
||||
const {
|
||||
selectedIds,
|
||||
toggle,
|
||||
selectAll,
|
||||
deselectAll,
|
||||
isSelected,
|
||||
count
|
||||
} = useSelection()
|
||||
|
||||
<Checkbox
|
||||
checked={isSelected(item.id)}
|
||||
onCheckedChange={() => toggle(item.id)}
|
||||
/>
|
||||
{count > 0 && <Badge>{count} selected</Badge>}
|
||||
```
|
||||
|
||||
### `useClipboard(successMessage)`
|
||||
|
||||
Copy to clipboard with feedback.
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useClipboard } from '@/hooks/ui'
|
||||
|
||||
const { copied, copy } = useClipboard('Copied to clipboard!')
|
||||
|
||||
<Button onClick={() => copy(codeSnippet)}>
|
||||
{copied ? 'Copied!' : 'Copy'}
|
||||
</Button>
|
||||
```
|
||||
|
||||
## Form Hooks
|
||||
|
||||
### `useFormField<T>(initialValue, rules)`
|
||||
|
||||
Single form field with validation.
|
||||
|
||||
**Features:**
|
||||
- Validation rules
|
||||
- Touch state
|
||||
- Error messages
|
||||
- Reset functionality
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useFormField } from '@/hooks/forms'
|
||||
|
||||
const email = useFormField('', [
|
||||
{
|
||||
validate: (v) => v.includes('@'),
|
||||
message: 'Invalid email'
|
||||
},
|
||||
{
|
||||
validate: (v) => v.length > 0,
|
||||
message: 'Email required'
|
||||
}
|
||||
])
|
||||
|
||||
<Input
|
||||
value={email.value}
|
||||
onChange={(e) => email.onChange(e.target.value)}
|
||||
onBlur={email.onBlur}
|
||||
/>
|
||||
{email.touched && email.error && <span>{email.error}</span>}
|
||||
```
|
||||
|
||||
### `useForm<T>(config)`
|
||||
|
||||
Complete form management with validation.
|
||||
|
||||
**Features:**
|
||||
- Multi-field state
|
||||
- Validation schema
|
||||
- Async submit handling
|
||||
- Touch tracking per field
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { useForm } from '@/hooks/forms'
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
name: '',
|
||||
email: '',
|
||||
message: ''
|
||||
},
|
||||
validate: (values) => {
|
||||
const errors: Record<string, string> = {}
|
||||
if (!values.email.includes('@')) {
|
||||
errors.email = 'Invalid email'
|
||||
}
|
||||
return errors
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
await api.submitForm(values)
|
||||
toast.success('Form submitted!')
|
||||
}
|
||||
})
|
||||
|
||||
<form onSubmit={form.handleSubmit}>
|
||||
<Input
|
||||
value={form.values.email}
|
||||
onChange={(e) => form.setValue('email', e.target.value)}
|
||||
/>
|
||||
{form.errors.email && <span>{form.errors.email}</span>}
|
||||
<Button type="submit" disabled={form.isSubmitting}>
|
||||
{form.isSubmitting ? 'Submitting...' : 'Submit'}
|
||||
</Button>
|
||||
</form>
|
||||
```
|
||||
|
||||
## Hook Composition
|
||||
|
||||
Hooks are designed to be composed together:
|
||||
|
||||
```typescript
|
||||
import { useArray, useSearch, useSort, usePagination } from '@/hooks/data'
|
||||
import { useSelection } from '@/hooks/ui'
|
||||
|
||||
function useProductList() {
|
||||
const { items, add, remove, update } = useArray<Product>('products', [])
|
||||
const { results, query, setQuery } = useSearch(items, ['name', 'category'])
|
||||
const { sortedItems, toggleSort } = useSort(results, 'name')
|
||||
const { items: pagedItems, ...pagination } = usePagination(sortedItems, 10)
|
||||
const selection = useSelection<Product>()
|
||||
|
||||
return {
|
||||
products: pagedItems,
|
||||
add,
|
||||
remove,
|
||||
update,
|
||||
search: { query, setQuery },
|
||||
sort: { toggleSort },
|
||||
pagination,
|
||||
selection,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Keep hooks focused**: One responsibility per hook
|
||||
2. **Use composition**: Combine simple hooks for complex behavior
|
||||
3. **Type everything**: Leverage TypeScript for safety
|
||||
4. **Handle loading/error**: Always consider async states
|
||||
5. **Memoize callbacks**: Use `useCallback` for stable references
|
||||
6. **Document dependencies**: Clear about what causes re-renders
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- Use `useCallback` and `useMemo` where appropriate
|
||||
- Avoid creating new objects/arrays in render
|
||||
- Leverage the functional update pattern for `setState`
|
||||
- Consider `useTransition` for non-urgent updates
|
||||
- Profile with React DevTools
|
||||
|
||||
## Testing Hooks
|
||||
|
||||
```typescript
|
||||
import { renderHook, act } from '@testing-library/react'
|
||||
import { useArray } from '@/hooks/data'
|
||||
|
||||
test('useArray adds items correctly', () => {
|
||||
const { result } = renderHook(() => useArray('test-key', []))
|
||||
|
||||
act(() => {
|
||||
result.current.add({ id: '1', name: 'Test' })
|
||||
})
|
||||
|
||||
expect(result.current.items).toHaveLength(1)
|
||||
expect(result.current.items[0].name).toBe('Test')
|
||||
})
|
||||
```
|
||||
411
docs/api/HOOK_LIBRARY_DOCS.md
Normal file
411
docs/api/HOOK_LIBRARY_DOCS.md
Normal file
@@ -0,0 +1,411 @@
|
||||
# Hook Library Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The CodeForge hook library provides a comprehensive set of reusable React hooks organized by purpose. All business logic should be extracted into hooks, keeping components under 150 LOC and focused purely on presentation.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
src/hooks/
|
||||
├── core/ # Core utility hooks
|
||||
├── ui/ # UI state management hooks
|
||||
├── config/ # Configuration and page management hooks
|
||||
├── features/ # Feature-specific business logic hooks
|
||||
├── ai/ # AI-powered functionality hooks
|
||||
└── validation/ # Validation and error checking hooks
|
||||
```
|
||||
|
||||
## Core Hooks
|
||||
|
||||
### `use-kv-state.ts`
|
||||
|
||||
Enhanced wrapper around Spark's `useKV` with Zod validation support.
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { useKVState } from '@/hooks/core/use-kv-state'
|
||||
import { z } from 'zod'
|
||||
|
||||
const UserSchema = z.object({
|
||||
name: z.string(),
|
||||
age: z.number().min(0),
|
||||
})
|
||||
|
||||
const [user, setUser] = useKVState(
|
||||
'user-data',
|
||||
{ name: '', age: 0 },
|
||||
UserSchema
|
||||
)
|
||||
|
||||
// Invalid data is rejected automatically
|
||||
setUser({ name: 'John', age: -5 }) // Logs error, keeps previous value
|
||||
setUser({ name: 'John', age: 25 }) // Valid, updates state
|
||||
```
|
||||
|
||||
### `use-debounced-save.ts`
|
||||
|
||||
Automatically debounces and saves values after a delay.
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { useDebouncedSave } from '@/hooks/core/use-debounced-save'
|
||||
import { useKV } from '@github/spark/hooks'
|
||||
|
||||
const [code, setCode] = useState('')
|
||||
const [, saveCode] = useKV('code-content', '')
|
||||
|
||||
useDebouncedSave(code, saveCode, 1000) // Saves 1s after last change
|
||||
```
|
||||
|
||||
### `use-clipboard.ts`
|
||||
|
||||
Copy/paste operations with user feedback.
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { useClipboard } from '@/hooks/core/use-clipboard'
|
||||
|
||||
const { copy, paste, copied } = useClipboard()
|
||||
|
||||
<Button
|
||||
onClick={() => copy(codeContent, 'Code copied!')}
|
||||
disabled={copied}
|
||||
>
|
||||
{copied ? 'Copied!' : 'Copy Code'}
|
||||
</Button>
|
||||
```
|
||||
|
||||
## UI Hooks
|
||||
|
||||
### `use-dialog.ts`
|
||||
|
||||
Manage dialog open/closed state.
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { useDialog } from '@/hooks/ui/use-dialog'
|
||||
|
||||
const { open, openDialog, closeDialog, toggleDialog } = useDialog()
|
||||
|
||||
<Button onClick={openDialog}>Open Settings</Button>
|
||||
<Dialog open={open} onOpenChange={closeDialog}>
|
||||
{/* Dialog content */}
|
||||
</Dialog>
|
||||
```
|
||||
|
||||
### `use-selection.ts`
|
||||
|
||||
Multi-select state management.
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { useSelection } from '@/hooks/ui/use-selection'
|
||||
|
||||
const {
|
||||
selected,
|
||||
toggle,
|
||||
selectAll,
|
||||
clear,
|
||||
isSelected,
|
||||
count
|
||||
} = useSelection<string>()
|
||||
|
||||
files.map(file => (
|
||||
<Checkbox
|
||||
checked={isSelected(file.id)}
|
||||
onCheckedChange={() => toggle(file.id)}
|
||||
/>
|
||||
))
|
||||
|
||||
<Button onClick={() => selectAll(files.map(f => f.id))}>
|
||||
Select All ({count} selected)
|
||||
</Button>
|
||||
```
|
||||
|
||||
### `use-confirmation.ts`
|
||||
|
||||
Confirmation dialog state and actions.
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { useConfirmation } from '@/hooks/ui/use-confirmation'
|
||||
import { AlertDialog } from '@/components/ui/alert-dialog'
|
||||
|
||||
const { state, confirm, handleConfirm, handleCancel } = useConfirmation()
|
||||
|
||||
const deleteFile = (fileId: string) => {
|
||||
confirm(
|
||||
'Delete File?',
|
||||
'This action cannot be undone.',
|
||||
() => {
|
||||
// Perform deletion
|
||||
setFiles(files.filter(f => f.id !== fileId))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
<AlertDialog open={state.open} onOpenChange={handleCancel}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogTitle>{state.title}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{state.description}</AlertDialogDescription>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={handleCancel}>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleConfirm}>Confirm</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
```
|
||||
|
||||
## Config Hooks
|
||||
|
||||
### `use-page-config.ts`
|
||||
|
||||
Load and manage page configurations from KV store.
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { usePageConfig, usePageRegistry } from '@/hooks/config/use-page-config'
|
||||
|
||||
// Get specific page config
|
||||
const { pageConfig } = usePageConfig('code-editor')
|
||||
console.log(pageConfig.title) // "Code Editor"
|
||||
|
||||
// Get all pages
|
||||
const { pages, getPage } = usePageRegistry()
|
||||
const dashboardPage = getPage('dashboard')
|
||||
```
|
||||
|
||||
### `use-layout-state.ts`
|
||||
|
||||
Persist layout state (panel sizes, collapsed state) per page.
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { useLayoutState } from '@/hooks/config/use-layout-state'
|
||||
|
||||
const { layoutState, setPanelSizes, setCollapsed, setActivePanel } =
|
||||
useLayoutState('code-editor')
|
||||
|
||||
<ResizablePanelGroup
|
||||
onLayout={setPanelSizes}
|
||||
defaultLayout={layoutState.panelSizes}
|
||||
>
|
||||
{/* Panels */}
|
||||
</ResizablePanelGroup>
|
||||
```
|
||||
|
||||
### `use-feature-flags.ts`
|
||||
|
||||
Runtime feature flag management.
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { useFeatureFlags } from '@/hooks/config/use-feature-flags'
|
||||
|
||||
const { isEnabled, enable, disable, toggle } = useFeatureFlags()
|
||||
|
||||
{isEnabled('ai-improve') && (
|
||||
<Button onClick={handleAIImprove}>
|
||||
AI Improve Code
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Switch
|
||||
checked={isEnabled('dark-mode')}
|
||||
onCheckedChange={() => toggle('dark-mode')}
|
||||
/>
|
||||
```
|
||||
|
||||
## Component Size Guidelines
|
||||
|
||||
### Rules
|
||||
1. **Maximum 150 LOC** per component file
|
||||
2. **Extract all business logic** into hooks
|
||||
3. **Pure presentation** in components
|
||||
4. **Compose smaller components** instead of conditional rendering
|
||||
|
||||
### Example: Breaking Down a Large Component
|
||||
|
||||
**Before (300+ LOC):**
|
||||
```typescript
|
||||
// ModelDesigner.tsx - 350 LOC
|
||||
export function ModelDesigner() {
|
||||
const [models, setModels] = useKV('models', [])
|
||||
const [selectedModel, setSelectedModel] = useState(null)
|
||||
const [showDialog, setShowDialog] = useState(false)
|
||||
|
||||
const handleAddModel = async () => {
|
||||
// 50 lines of logic
|
||||
}
|
||||
|
||||
const handleGenerateWithAI = async () => {
|
||||
// 50 lines of AI logic
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* 200 lines of JSX */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**After (<150 LOC each):**
|
||||
```typescript
|
||||
// hooks/features/use-model-manager.ts - 80 LOC
|
||||
export function useModelManager() {
|
||||
const [models, setModels] = useKV('models', [])
|
||||
const [selectedModel, setSelectedModel] = useState(null)
|
||||
|
||||
const addModel = useCallback(async (model) => {
|
||||
// Business logic
|
||||
}, [])
|
||||
|
||||
const generateWithAI = useCallback(async (description) => {
|
||||
// AI logic
|
||||
}, [])
|
||||
|
||||
return { models, selectedModel, setSelectedModel, addModel, generateWithAI }
|
||||
}
|
||||
|
||||
// components/ModelList.tsx - 80 LOC
|
||||
export function ModelList({ models, onSelect, selected }) {
|
||||
return (
|
||||
<ScrollArea>
|
||||
{models.map(model => (
|
||||
<ModelCard
|
||||
key={model.id}
|
||||
model={model}
|
||||
selected={selected?.id === model.id}
|
||||
onClick={() => onSelect(model)}
|
||||
/>
|
||||
))}
|
||||
</ScrollArea>
|
||||
)
|
||||
}
|
||||
|
||||
// components/ModelEditor.tsx - 120 LOC
|
||||
export function ModelEditor({ model, onChange }) {
|
||||
return (
|
||||
<Card>
|
||||
{/* Editing UI */}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
// ModelDesigner.tsx - 90 LOC
|
||||
export function ModelDesigner() {
|
||||
const { models, selectedModel, setSelectedModel, addModel, generateWithAI } =
|
||||
useModelManager()
|
||||
const { open, openDialog, closeDialog } = useDialog()
|
||||
|
||||
return (
|
||||
<div className="flex gap-4">
|
||||
<ModelList
|
||||
models={models}
|
||||
selected={selectedModel}
|
||||
onSelect={setSelectedModel}
|
||||
/>
|
||||
{selectedModel && (
|
||||
<ModelEditor
|
||||
model={selectedModel}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
)}
|
||||
<Dialog open={open} onOpenChange={closeDialog}>
|
||||
{/* AI Dialog */}
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## JSON-Based Page Orchestration
|
||||
|
||||
### Page Config Schema
|
||||
|
||||
Pages can be defined in JSON and stored in the KV database:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "code-editor",
|
||||
"title": "Code Editor",
|
||||
"description": "Edit project files with AI assistance",
|
||||
"icon": "Code",
|
||||
"component": "CodeEditorPage",
|
||||
"layout": {
|
||||
"type": "split",
|
||||
"direction": "horizontal",
|
||||
"defaultSizes": [20, 80],
|
||||
"panels": [
|
||||
{
|
||||
"id": "file-tree",
|
||||
"component": "FileExplorer",
|
||||
"minSize": 15,
|
||||
"maxSize": 40
|
||||
},
|
||||
{
|
||||
"id": "editor",
|
||||
"component": "CodeEditor",
|
||||
"minSize": 60
|
||||
}
|
||||
]
|
||||
},
|
||||
"features": [
|
||||
{ "id": "ai-improve", "enabled": true },
|
||||
{ "id": "ai-explain", "enabled": true }
|
||||
],
|
||||
"shortcuts": [
|
||||
{ "key": "2", "ctrl": true, "action": "navigate" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
1. **Runtime Configuration**: Change page layouts without code changes
|
||||
2. **User Customization**: Users can configure their own layouts
|
||||
3. **Feature Flags**: Enable/disable features per page
|
||||
4. **A/B Testing**: Test different layouts easily
|
||||
5. **Persistence**: Layouts save automatically to KV store
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
When refactoring a large component:
|
||||
|
||||
- [ ] Identify business logic vs presentation
|
||||
- [ ] Extract business logic into hooks (`/hooks/features/`)
|
||||
- [ ] Break JSX into smaller components (<150 LOC each)
|
||||
- [ ] Create atomic components where applicable
|
||||
- [ ] Add component to page config JSON
|
||||
- [ ] Update component registry
|
||||
- [ ] Test isolated components
|
||||
- [ ] Update documentation
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **One Hook, One Responsibility**: Each hook should manage one concern
|
||||
2. **Hooks Return Objects**: Return `{ value, actions }` for clarity
|
||||
3. **Memoize Callbacks**: Use `useCallback` for functions passed as props
|
||||
4. **Type Everything**: Full TypeScript types for all hooks
|
||||
5. **Test Hooks**: Write unit tests for hook logic
|
||||
6. **Document Hooks**: Add JSDoc comments explaining purpose and usage
|
||||
7. **Validate with Zod**: Use schemas for complex data structures
|
||||
8. **Handle Loading/Error States**: Return `{ data, loading, error }` pattern
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Extract business logic from top 5 largest components
|
||||
2. Break down FeatureIdeaCloud (829 LOC) into 6 smaller components
|
||||
3. Create remaining feature hooks (file-manager, workflow-manager, etc.)
|
||||
4. Build PageOrchestrator component to render from JSON configs
|
||||
5. Add UI for editing page layouts
|
||||
6. Migrate all pages to JSON configuration system
|
||||
|
||||
## Resources
|
||||
|
||||
- [REFACTOR_PHASE2.md](./REFACTOR_PHASE2.md) - Complete refactoring plan
|
||||
- [React Hooks Documentation](https://react.dev/reference/react/hooks)
|
||||
- [Zod Documentation](https://zod.dev/)
|
||||
- [Atomic Design Methodology](https://bradfrost.com/blog/post/atomic-web-design/)
|
||||
760
docs/api/HOOK_LIBRARY_REFERENCE.md
Normal file
760
docs/api/HOOK_LIBRARY_REFERENCE.md
Normal file
@@ -0,0 +1,760 @@
|
||||
# Hook Library Reference
|
||||
|
||||
## Overview
|
||||
|
||||
The CodeForge hook library provides a comprehensive set of reusable hooks for managing application state, UI interactions, and business logic. All hooks follow React best practices and are fully typed with TypeScript.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Data Management Hooks](#data-management-hooks)
|
||||
2. [Core Utility Hooks](#core-utility-hooks)
|
||||
3. [UI State Hooks](#ui-state-hooks)
|
||||
4. [Form Hooks](#form-hooks)
|
||||
5. [Canvas Hooks](#canvas-hooks)
|
||||
6. [AI Hooks](#ai-hooks)
|
||||
7. [Orchestration Hooks](#orchestration-hooks)
|
||||
|
||||
## Data Management Hooks
|
||||
|
||||
### useFiles
|
||||
|
||||
Manage project files with full CRUD operations.
|
||||
|
||||
```typescript
|
||||
import { useFiles } from '@/hooks/data/use-files'
|
||||
|
||||
function MyComponent() {
|
||||
const {
|
||||
files, // ProjectFile[]
|
||||
addFile, // (file: ProjectFile) => void
|
||||
updateFile, // (id: string, updates: Partial<ProjectFile>) => void
|
||||
deleteFile, // (id: string) => void
|
||||
getFile, // (id: string) => ProjectFile | undefined
|
||||
updateFileContent // (id: string, content: string) => void
|
||||
} = useFiles()
|
||||
|
||||
// Usage
|
||||
addFile({
|
||||
id: 'new-file',
|
||||
name: 'App.tsx',
|
||||
path: '/src/App.tsx',
|
||||
content: 'export default function App() {}',
|
||||
language: 'typescript'
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### useModels
|
||||
|
||||
Manage Prisma models.
|
||||
|
||||
```typescript
|
||||
import { useModels } from '@/hooks/data/use-models'
|
||||
|
||||
function ModelDesigner() {
|
||||
const {
|
||||
models, // PrismaModel[]
|
||||
addModel, // (model: PrismaModel) => void
|
||||
updateModel, // (id: string, updates: Partial<PrismaModel>) => void
|
||||
deleteModel, // (id: string) => void
|
||||
getModel // (id: string) => PrismaModel | undefined
|
||||
} = useModels()
|
||||
|
||||
// Add a new model
|
||||
addModel({
|
||||
id: 'user-model',
|
||||
name: 'User',
|
||||
fields: [
|
||||
{ name: 'id', type: 'String', isId: true },
|
||||
{ name: 'email', type: 'String', isUnique: true }
|
||||
],
|
||||
relations: []
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### useComponents
|
||||
|
||||
Manage React components.
|
||||
|
||||
```typescript
|
||||
import { useComponents } from '@/hooks/data/use-components'
|
||||
|
||||
function ComponentBuilder() {
|
||||
const {
|
||||
components, // ComponentNode[]
|
||||
addComponent, // (component: ComponentNode) => void
|
||||
updateComponent, // (id: string, updates: Partial<ComponentNode>) => void
|
||||
deleteComponent, // (id: string) => void
|
||||
getComponent // (id: string) => ComponentNode | undefined
|
||||
} = useComponents()
|
||||
}
|
||||
```
|
||||
|
||||
### useWorkflows
|
||||
|
||||
Manage n8n-style workflows.
|
||||
|
||||
```typescript
|
||||
import { useWorkflows } from '@/hooks/data/use-workflows'
|
||||
|
||||
function WorkflowDesigner() {
|
||||
const {
|
||||
workflows, // Workflow[]
|
||||
addWorkflow, // (workflow: Workflow) => void
|
||||
updateWorkflow, // (id: string, updates: Partial<Workflow>) => void
|
||||
deleteWorkflow, // (id: string) => void
|
||||
getWorkflow // (id: string) => Workflow | undefined
|
||||
} = useWorkflows()
|
||||
}
|
||||
```
|
||||
|
||||
### useLambdas
|
||||
|
||||
Manage serverless functions.
|
||||
|
||||
```typescript
|
||||
import { useLambdas } from '@/hooks/data/use-lambdas'
|
||||
|
||||
function LambdaDesigner() {
|
||||
const {
|
||||
lambdas, // Lambda[]
|
||||
addLambda, // (lambda: Lambda) => void
|
||||
updateLambda, // (id: string, updates: Partial<Lambda>) => void
|
||||
deleteLambda, // (id: string) => void
|
||||
getLambda // (id: string) => Lambda | undefined
|
||||
} = useLambdas()
|
||||
}
|
||||
```
|
||||
|
||||
## Core Utility Hooks
|
||||
|
||||
### useKVState
|
||||
|
||||
Enhanced KV storage with validation and transformations.
|
||||
|
||||
```typescript
|
||||
import { useKVState } from '@/hooks/core/use-kv-state'
|
||||
|
||||
function MyComponent() {
|
||||
const [value, setValue] = useKVState({
|
||||
key: 'my-data',
|
||||
defaultValue: { count: 0 },
|
||||
validate: (data) => data.count >= 0,
|
||||
transform: (data) => ({ ...data, lastUpdated: Date.now() })
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### useClipboard
|
||||
|
||||
Copy to clipboard with feedback.
|
||||
|
||||
```typescript
|
||||
import { useClipboard } from '@/hooks/core/use-clipboard'
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const { copy, copied } = useClipboard()
|
||||
|
||||
return (
|
||||
<button onClick={() => copy(text)}>
|
||||
{copied ? 'Copied!' : 'Copy'}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### useDebouncedSave
|
||||
|
||||
Auto-save with debouncing.
|
||||
|
||||
```typescript
|
||||
import { useDebouncedSave } from '@/hooks/core/use-debounced-save'
|
||||
|
||||
function Editor() {
|
||||
const [content, setContent] = useState('')
|
||||
|
||||
useDebouncedSave(content, async (value) => {
|
||||
await saveToServer(value)
|
||||
}, 1000) // 1 second delay
|
||||
|
||||
return <textarea value={content} onChange={e => setContent(e.target.value)} />
|
||||
}
|
||||
```
|
||||
|
||||
## UI State Hooks
|
||||
|
||||
### useDialog
|
||||
|
||||
Manage dialog/modal state.
|
||||
|
||||
```typescript
|
||||
import { useDialog } from '@/hooks/ui/use-dialog'
|
||||
|
||||
function MyComponent() {
|
||||
const { isOpen, open, close, toggle } = useDialog()
|
||||
|
||||
return (
|
||||
<>
|
||||
<button onClick={open}>Open Dialog</button>
|
||||
<Dialog open={isOpen} onOpenChange={close}>
|
||||
{/* Dialog content */}
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### useConfirmation
|
||||
|
||||
Confirm dangerous actions.
|
||||
|
||||
```typescript
|
||||
import { useConfirmation } from '@/hooks/ui/use-confirmation'
|
||||
|
||||
function DeleteButton({ onDelete }: { onDelete: () => void }) {
|
||||
const { confirm, ConfirmDialog } = useConfirmation({
|
||||
title: 'Delete Item',
|
||||
description: 'Are you sure? This cannot be undone.',
|
||||
confirmText: 'Delete',
|
||||
variant: 'destructive'
|
||||
})
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (await confirm()) {
|
||||
onDelete()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<button onClick={handleDelete}>Delete</button>
|
||||
<ConfirmDialog />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### useSelection
|
||||
|
||||
Manage item selection (single or multiple).
|
||||
|
||||
```typescript
|
||||
import { useSelection } from '@/hooks/ui/use-selection'
|
||||
|
||||
function ItemList({ items }: { items: Item[] }) {
|
||||
const {
|
||||
selected, // Set<string>
|
||||
isSelected, // (id: string) => boolean
|
||||
toggle, // (id: string) => void
|
||||
selectAll, // () => void
|
||||
deselectAll, // () => void
|
||||
selectedCount // number
|
||||
} = useSelection({ multi: true })
|
||||
|
||||
return (
|
||||
<div>
|
||||
{items.map(item => (
|
||||
<div
|
||||
key={item.id}
|
||||
onClick={() => toggle(item.id)}
|
||||
className={isSelected(item.id) ? 'selected' : ''}
|
||||
>
|
||||
{item.name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### useModal
|
||||
|
||||
Enhanced modal with data passing.
|
||||
|
||||
```typescript
|
||||
import { useModal } from '@/hooks/ui/use-modal'
|
||||
|
||||
function App() {
|
||||
const { isOpen, data, open, close } = useModal<{ userId: string }>()
|
||||
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => open({ userId: '123' })}>
|
||||
Edit User
|
||||
</button>
|
||||
{isOpen && <UserModal userId={data?.userId} onClose={close} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### useTabs
|
||||
|
||||
Manage tab state with URL sync.
|
||||
|
||||
```typescript
|
||||
import { useTabs } from '@/hooks/ui/use-tabs'
|
||||
|
||||
function TabbedInterface() {
|
||||
const { activeTab, setTab, tabs } = useTabs({
|
||||
tabs: ['overview', 'details', 'settings'],
|
||||
defaultTab: 'overview',
|
||||
syncWithUrl: true
|
||||
})
|
||||
|
||||
return (
|
||||
<Tabs value={activeTab} onValueChange={setTab}>
|
||||
{/* Tab content */}
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### usePanels
|
||||
|
||||
Manage resizable panel state.
|
||||
|
||||
```typescript
|
||||
import { usePanels } from '@/hooks/ui/use-panels'
|
||||
|
||||
function SplitView() {
|
||||
const { sizes, setSizes, collapsed, togglePanel } = usePanels({
|
||||
defaultSizes: [30, 70],
|
||||
minSizes: [20, 50]
|
||||
})
|
||||
|
||||
return (
|
||||
<ResizablePanelGroup sizes={sizes} onResize={setSizes}>
|
||||
{/* Panels */}
|
||||
</ResizablePanelGroup>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Form Hooks
|
||||
|
||||
### useFormState
|
||||
|
||||
Comprehensive form state management.
|
||||
|
||||
```typescript
|
||||
import { useFormState } from '@/hooks/forms/use-form-state'
|
||||
|
||||
function UserForm() {
|
||||
const {
|
||||
values, // Current form values
|
||||
errors, // Validation errors
|
||||
touched, // Touched fields
|
||||
isValid, // Form validity
|
||||
isDirty, // Form has changes
|
||||
isSubmitting, // Submission state
|
||||
setValue, // Set single field
|
||||
setValues, // Set multiple fields
|
||||
setError, // Set error
|
||||
handleSubmit, // Submit handler
|
||||
reset // Reset form
|
||||
} = useFormState({
|
||||
initialValues: {
|
||||
name: '',
|
||||
email: ''
|
||||
},
|
||||
validate: (values) => {
|
||||
const errors: any = {}
|
||||
if (!values.email) errors.email = 'Required'
|
||||
return errors
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
await saveUser(values)
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
value={values.name}
|
||||
onChange={e => setValue('name', e.target.value)}
|
||||
/>
|
||||
{errors.name && <span>{errors.name}</span>}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### useValidation
|
||||
|
||||
Reusable validation logic.
|
||||
|
||||
```typescript
|
||||
import { useValidation } from '@/hooks/forms/use-validation'
|
||||
|
||||
function PasswordField() {
|
||||
const { validate, errors, isValid } = useValidation({
|
||||
rules: {
|
||||
minLength: 8,
|
||||
hasNumber: true,
|
||||
hasSpecialChar: true
|
||||
}
|
||||
})
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
validate(value)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### useFieldArray
|
||||
|
||||
Manage dynamic form arrays.
|
||||
|
||||
```typescript
|
||||
import { useFieldArray } from '@/hooks/forms/use-field-array'
|
||||
|
||||
function ContactsForm() {
|
||||
const {
|
||||
fields, // Array of field values
|
||||
append, // Add new field
|
||||
remove, // Remove field
|
||||
move, // Reorder fields
|
||||
update // Update specific field
|
||||
} = useFieldArray({
|
||||
name: 'contacts',
|
||||
defaultValue: []
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<div key={index}>
|
||||
<input value={field.email} />
|
||||
<button onClick={() => remove(index)}>Remove</button>
|
||||
</div>
|
||||
))}
|
||||
<button onClick={() => append({ email: '' })}>
|
||||
Add Contact
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Canvas Hooks
|
||||
|
||||
### useCanvas
|
||||
|
||||
Canvas drawing utilities.
|
||||
|
||||
```typescript
|
||||
import { useCanvas } from '@/hooks/canvas/use-canvas'
|
||||
|
||||
function DrawingCanvas() {
|
||||
const {
|
||||
canvasRef,
|
||||
ctx,
|
||||
clear,
|
||||
drawLine,
|
||||
drawRect,
|
||||
drawCircle,
|
||||
getImageData,
|
||||
setImageData
|
||||
} = useCanvas({
|
||||
width: 800,
|
||||
height: 600
|
||||
})
|
||||
|
||||
return <canvas ref={canvasRef} />
|
||||
}
|
||||
```
|
||||
|
||||
### useDragDrop
|
||||
|
||||
Drag and drop for canvas elements.
|
||||
|
||||
```typescript
|
||||
import { useDragDrop } from '@/hooks/canvas/use-drag-drop'
|
||||
|
||||
function DraggableElements() {
|
||||
const {
|
||||
elements, // Array of positioned elements
|
||||
draggedId, // Currently dragged element
|
||||
handleMouseDown, // Start drag
|
||||
handleMouseMove, // During drag
|
||||
handleMouseUp, // End drag
|
||||
updatePosition, // Programmatic position update
|
||||
} = useDragDrop({
|
||||
initialElements: [
|
||||
{ id: '1', x: 100, y: 100, width: 50, height: 50 }
|
||||
]
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### useZoomPan
|
||||
|
||||
Zoom and pan for canvas views.
|
||||
|
||||
```typescript
|
||||
import { useZoomPan } from '@/hooks/canvas/use-zoom-pan'
|
||||
|
||||
function ZoomableCanvas() {
|
||||
const {
|
||||
zoom, // Current zoom level
|
||||
pan, // Pan offset { x, y }
|
||||
zoomIn, // Increase zoom
|
||||
zoomOut, // Decrease zoom
|
||||
resetZoom, // Reset to default
|
||||
setPan, // Set pan position
|
||||
transform // CSS transform string
|
||||
} = useZoomPan({
|
||||
minZoom: 0.1,
|
||||
maxZoom: 5,
|
||||
defaultZoom: 1
|
||||
})
|
||||
|
||||
return (
|
||||
<div style={{ transform }}>
|
||||
{/* Canvas content */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### useConnections
|
||||
|
||||
Manage node connections (for diagrams/workflows).
|
||||
|
||||
```typescript
|
||||
import { useConnections } from '@/hooks/canvas/use-connections'
|
||||
|
||||
function FlowDiagram() {
|
||||
const {
|
||||
connections, // Array of connections
|
||||
addConnection, // Add new connection
|
||||
removeConnection, // Remove connection
|
||||
updateConnection, // Update connection
|
||||
getConnections // Get connections for node
|
||||
} = useConnections({
|
||||
validate: (from, to) => from !== to // No self-connections
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## AI Hooks
|
||||
|
||||
### useAIGenerate
|
||||
|
||||
Generate content with AI.
|
||||
|
||||
```typescript
|
||||
import { useAIGenerate } from '@/hooks/ai/use-ai-generate'
|
||||
|
||||
function AIAssistant() {
|
||||
const {
|
||||
generate, // (prompt: string) => Promise<string>
|
||||
isGenerating, // boolean
|
||||
result, // Generated content
|
||||
error // Error if any
|
||||
} = useAIGenerate()
|
||||
|
||||
const handleGenerate = async () => {
|
||||
const code = await generate('Create a React login form')
|
||||
console.log(code)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### useAIComplete
|
||||
|
||||
AI code completion.
|
||||
|
||||
```typescript
|
||||
import { useAIComplete } from '@/hooks/ai/use-ai-complete'
|
||||
|
||||
function CodeEditor() {
|
||||
const {
|
||||
complete, // (prefix: string) => Promise<string>
|
||||
suggestions, // string[]
|
||||
isCompleting, // boolean
|
||||
acceptSuggestion // (index: number) => void
|
||||
} = useAIComplete({
|
||||
debounce: 300
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### useAISuggestions
|
||||
|
||||
Get AI suggestions for improvements.
|
||||
|
||||
```typescript
|
||||
import { useAISuggestions } from '@/hooks/ai/use-ai-suggestions'
|
||||
|
||||
function CodeReview() {
|
||||
const {
|
||||
getSuggestions, // (code: string) => Promise<Suggestion[]>
|
||||
suggestions, // Suggestion[]
|
||||
applySuggestion, // (id: string) => void
|
||||
dismissSuggestion // (id: string) => void
|
||||
} = useAISuggestions()
|
||||
}
|
||||
```
|
||||
|
||||
## Orchestration Hooks
|
||||
|
||||
### usePage
|
||||
|
||||
Execute a page schema.
|
||||
|
||||
```typescript
|
||||
import { usePage } from '@/hooks/orchestration/use-page'
|
||||
import pageSchema from '@/config/pages/my-page.json'
|
||||
|
||||
function DynamicPage() {
|
||||
const {
|
||||
context, // Data context
|
||||
execute, // Execute action by ID
|
||||
isExecuting, // boolean
|
||||
handlers, // Action handlers map
|
||||
schema // Page schema
|
||||
} = usePage(pageSchema)
|
||||
|
||||
return <PageRenderer schema={schema} />
|
||||
}
|
||||
```
|
||||
|
||||
### useActions
|
||||
|
||||
Execute page actions.
|
||||
|
||||
```typescript
|
||||
import { useActions } from '@/hooks/orchestration/use-actions'
|
||||
|
||||
function ActionButtons() {
|
||||
const { execute, isExecuting, handlers } = useActions(
|
||||
[
|
||||
{
|
||||
id: 'save',
|
||||
type: 'update',
|
||||
params: { target: 'Files' }
|
||||
}
|
||||
],
|
||||
{ files: [], setFiles: () => {} }
|
||||
)
|
||||
|
||||
return (
|
||||
<button onClick={() => execute('save')} disabled={isExecuting}>
|
||||
Save
|
||||
</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Hook Composition Patterns
|
||||
|
||||
### Combining Multiple Hooks
|
||||
|
||||
```typescript
|
||||
function FeaturePage() {
|
||||
const files = useFiles()
|
||||
const models = useModels()
|
||||
const { isOpen, open, close } = useDialog()
|
||||
const { selected, toggle } = useSelection()
|
||||
|
||||
// Combine hooks to build complex features
|
||||
const handleCreateModel = () => {
|
||||
const model = createModelFromFiles(files.files)
|
||||
models.addModel(model)
|
||||
close()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Hook Composition
|
||||
|
||||
```typescript
|
||||
function useProjectEditor() {
|
||||
const files = useFiles()
|
||||
const { isOpen, open, close } = useDialog()
|
||||
const [activeFileId, setActiveFileId] = useState<string | null>(null)
|
||||
|
||||
const openFile = (id: string) => {
|
||||
setActiveFileId(id)
|
||||
open()
|
||||
}
|
||||
|
||||
const saveFile = (content: string) => {
|
||||
if (activeFileId) {
|
||||
files.updateFileContent(activeFileId, content)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
files: files.files,
|
||||
activeFile: files.getFile(activeFileId),
|
||||
openFile,
|
||||
saveFile,
|
||||
isEditing: isOpen,
|
||||
closeEditor: close
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Always Use Functional Updates
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - May cause data loss
|
||||
setFiles([...files, newFile])
|
||||
|
||||
// ✅ Good - Always gets current state
|
||||
setFiles(current => [...current, newFile])
|
||||
```
|
||||
|
||||
### 2. Memoize Expensive Computations
|
||||
|
||||
```typescript
|
||||
const filteredFiles = useMemo(() => {
|
||||
return files.filter(f => f.name.includes(search))
|
||||
}, [files, search])
|
||||
```
|
||||
|
||||
### 3. Clean Up Side Effects
|
||||
|
||||
```typescript
|
||||
useEffect(() => {
|
||||
const subscription = subscribe()
|
||||
return () => subscription.unsubscribe()
|
||||
}, [])
|
||||
```
|
||||
|
||||
### 4. Type Your Hooks
|
||||
|
||||
```typescript
|
||||
function useTypedData<T>(key: string, defaultValue: T) {
|
||||
const [data, setData] = useKV<T>(key, defaultValue)
|
||||
return { data, setData }
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Extract Reusable Logic
|
||||
|
||||
```typescript
|
||||
// Extract common patterns into custom hooks
|
||||
function useResource<T>(resourceName: string) {
|
||||
const [items, setItems] = useKV<T[]>(`${resourceName}-items`, [])
|
||||
|
||||
const add = useCallback((item: T) => {
|
||||
setItems(current => [...current, item])
|
||||
}, [setItems])
|
||||
|
||||
return { items, add }
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Explore `/src/hooks/` directory for implementations
|
||||
- Check `../architecture/JSON_ORCHESTRATION_GUIDE.md` for page schemas
|
||||
- See `REFACTOR_PHASE3.md` for architecture overview
|
||||
- Read individual hook files for detailed implementation notes
|
||||
Reference in New Issue
Block a user