@objectstack/client-react 0.8.2 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +151 -0
  3. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @objectstack/client-react
2
2
 
3
+ ## 0.9.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Patch release for maintenance and stability improvements. All packages updated with unified versioning.
8
+ - Updated dependencies
9
+ - @objectstack/spec@0.9.1
10
+ - @objectstack/core@0.9.1
11
+ - @objectstack/client@0.9.1
12
+
3
13
  ## 0.8.2
4
14
 
5
15
  ### Patch Changes
package/README.md CHANGED
@@ -268,6 +268,157 @@ const { mutate } = useMutation<Task, Partial<Task>>('todo_task', 'create');
268
268
  // mutate expects Partial<Task>
269
269
  ```
270
270
 
271
+ ## Common Patterns
272
+
273
+ ### Master-Detail View
274
+
275
+ ```tsx
276
+ function TaskList() {
277
+ const [selectedId, setSelectedId] = useState<string | null>(null);
278
+
279
+ const { data: tasks } = useQuery('todo_task', {
280
+ select: ['id', 'subject'],
281
+ sort: ['-created_at']
282
+ });
283
+
284
+ const { data: selectedTask } = useQuery('todo_task', {
285
+ filters: ['id', '=', selectedId],
286
+ enabled: !!selectedId // Only fetch when ID is selected
287
+ });
288
+
289
+ return (
290
+ <div className="flex">
291
+ <TaskListPanel tasks={tasks?.value} onSelect={setSelectedId} />
292
+ <TaskDetail task={selectedTask?.value?.[0]} />
293
+ </div>
294
+ );
295
+ }
296
+ ```
297
+
298
+ ### Optimistic Updates
299
+
300
+ ```tsx
301
+ function TaskToggle({ taskId, completed }) {
302
+ const { mutate } = useMutation('todo_task', 'update', {
303
+ onMutate: async (variables) => {
304
+ // Optimistically update UI
305
+ return { previousValue: completed };
306
+ },
307
+ onError: (error, variables, context) => {
308
+ // Revert on error
309
+ console.error('Update failed, reverting', context.previousValue);
310
+ },
311
+ onSuccess: () => {
312
+ // Refetch to ensure data consistency
313
+ queryClient.invalidateQueries(['todo_task']);
314
+ }
315
+ });
316
+
317
+ return (
318
+ <Checkbox
319
+ checked={completed}
320
+ onChange={(e) => mutate({ id: taskId, is_completed: e.target.checked })}
321
+ />
322
+ );
323
+ }
324
+ ```
325
+
326
+ ### Dependent Queries
327
+
328
+ ```tsx
329
+ function ProjectTasks({ projectId }) {
330
+ // First, get project details
331
+ const { data: project } = useQuery('project', {
332
+ filters: ['id', '=', projectId]
333
+ });
334
+
335
+ // Then, get tasks for this project
336
+ const { data: tasks } = useQuery('todo_task', {
337
+ filters: ['project_id', '=', projectId],
338
+ enabled: !!project // Only fetch when project is loaded
339
+ });
340
+
341
+ return (
342
+ <div>
343
+ <h2>{project?.value?.[0]?.name}</h2>
344
+ <TaskList tasks={tasks?.value} />
345
+ </div>
346
+ );
347
+ }
348
+ ```
349
+
350
+ ### Search with Debounce
351
+
352
+ ```tsx
353
+ import { useDeferredValue } from 'react';
354
+
355
+ function TaskSearch() {
356
+ const [searchTerm, setSearchTerm] = useState('');
357
+ const deferredSearch = useDeferredValue(searchTerm);
358
+
359
+ const { data, isLoading } = useQuery('todo_task', {
360
+ filters: ['subject', 'contains', deferredSearch],
361
+ enabled: deferredSearch.length >= 3 // Only search with 3+ chars
362
+ });
363
+
364
+ return (
365
+ <div>
366
+ <input
367
+ type="text"
368
+ value={searchTerm}
369
+ onChange={(e) => setSearchTerm(e.target.value)}
370
+ placeholder="Search tasks..."
371
+ />
372
+ {isLoading && <Spinner />}
373
+ <TaskList tasks={data?.value} />
374
+ </div>
375
+ );
376
+ }
377
+ ```
378
+
379
+ ### Form with Validation
380
+
381
+ ```tsx
382
+ function TaskForm() {
383
+ const { mutate, isLoading, error } = useMutation('todo_task', 'create', {
384
+ onSuccess: () => {
385
+ router.push('/tasks');
386
+ }
387
+ });
388
+
389
+ const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
390
+ e.preventDefault();
391
+ const formData = new FormData(e.currentTarget);
392
+
393
+ mutate({
394
+ subject: formData.get('subject'),
395
+ priority: Number(formData.get('priority')),
396
+ due_date: formData.get('due_date')
397
+ });
398
+ };
399
+
400
+ return (
401
+ <form onSubmit={handleSubmit}>
402
+ <input name="subject" required />
403
+ <input name="priority" type="number" min="1" max="5" />
404
+ <input name="due_date" type="date" />
405
+
406
+ {error && (
407
+ <div className="error">
408
+ {error.code === 'validation_error'
409
+ ? 'Please check your input'
410
+ : error.message}
411
+ </div>
412
+ )}
413
+
414
+ <button type="submit" disabled={isLoading}>
415
+ {isLoading ? 'Creating...' : 'Create Task'}
416
+ </button>
417
+ </form>
418
+ );
419
+ }
420
+ ```
421
+
271
422
  ## License
272
423
 
273
424
  Apache-2.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/client-react",
3
- "version": "0.8.2",
3
+ "version": "0.9.1",
4
4
  "description": "React hooks for ObjectStack Client SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,9 +8,9 @@
8
8
  "react": ">=18.0.0"
9
9
  },
10
10
  "dependencies": {
11
- "@objectstack/client": "0.8.2",
12
- "@objectstack/spec": "0.8.2",
13
- "@objectstack/core": "0.8.2"
11
+ "@objectstack/client": "0.9.1",
12
+ "@objectstack/spec": "0.9.1",
13
+ "@objectstack/core": "0.9.1"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/react": "^18.0.0",