You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add 'Hook Usage by Component Tier' section to hooks.md explaining that
primitives are pure (no hooks), widgets wire data hooks to primitives,
and views compose widgets with imperative/guard hooks.
Update components.md widget descriptions with granular hook names
(useEventList, useBalance, etc. instead of monolithic useEvents) and
add code example for PredictHome view showing widget composition.
Update hook composition rules to reflect the tier boundaries.
Widgets combine Tier 1 primitives with deep hooks from [hooks](./hooks.md). Each widget maps to a major screen section and contains the section-level state needed to operate.
416
+
Widgets are the integration layer between data and presentation. They call data query hooks internally and render Tier 1 primitives with the results. Each widget maps to a major screen section and owns the section-level state needed to operate.
417
+
418
+
See [hooks — Hook Usage by Component Tier](./hooks.md#hook-usage-by-component-tier) for the full tier/hook relationship.
416
419
417
420
### EventFeed
418
421
@@ -425,16 +428,17 @@ Composes:
425
428
-`EventCard`
426
429
-`Skeleton`
427
430
428
-
Hooks:
431
+
Hooks (called internally by the widget):
429
432
430
-
-`useEvents`
431
-
- optional local filter state hook inside the widget
433
+
-`useEventList` from `hooks/events` — paginated event feed
434
+
-`useEventSearch` from `hooks/events` — search results
435
+
- optional local filter/tab state hook co-located with the widget
432
436
433
437
Typical responsibilities:
434
438
435
439
- search box input
436
440
- category tab state
437
-
- pagination trigger
441
+
- pagination trigger via `fetchMore`
438
442
- empty and loading states
439
443
440
444
### FeaturedCarousel
@@ -447,10 +451,10 @@ Composes:
447
451
448
452
-`EventCard`
449
453
450
-
Hooks:
454
+
Hooks (called internally by the widget):
451
455
452
-
-`useEvents` for `featured`
453
-
-`usePredictNavigation`
456
+
-`useFeaturedEvents` from `hooks/events` — carousel events
457
+
-`usePredictNavigation` from `hooks/navigation` — tap-to-details navigation
454
458
455
459
Typical responsibilities:
456
460
@@ -470,10 +474,11 @@ Composes:
470
474
-`PriceDisplay`
471
475
-`Skeleton`
472
476
473
-
Hooks:
477
+
Hooks (called internally by the widget):
474
478
475
-
-`usePortfolio`
476
-
-`useTransactions`
479
+
-`usePositions` from `hooks/portfolio` — open and resolved positions
480
+
-`useBalance` from `hooks/portfolio` — prediction market balance
481
+
-`usePnL` from `hooks/portfolio` — unrealized P&L
477
482
478
483
Typical responsibilities:
479
484
@@ -492,11 +497,11 @@ Composes:
492
497
-`OutcomeButton`
493
498
-`PriceDisplay`
494
499
495
-
Hooks:
500
+
Hooks (called internally by the widget):
496
501
497
-
-`useTrading`
498
-
-`usePredictGuard`
499
-
- local keypad state hook
502
+
-`useTrading` from `hooks/trading` — order preview and placement
503
+
-`usePredictGuard` from `hooks/guard` — eligibility check
504
+
- local keypad state hook co-located with the widget
500
505
501
506
Typical responsibilities:
502
507
@@ -516,10 +521,10 @@ Composes:
516
521
-`PriceDisplay`
517
522
-`Skeleton`
518
523
519
-
Hooks:
524
+
Hooks (called internally by the widget):
520
525
521
-
-`usePortfolio`
522
-
-`usePredictNavigation`
526
+
-`useActivity` from `hooks/portfolio` — transaction history
527
+
-`usePredictNavigation` from `hooks/navigation` — detail sheet navigation
523
528
524
529
Typical responsibilities:
525
530
@@ -529,25 +534,40 @@ Typical responsibilities:
529
534
530
535
## Tier 3: Views
531
536
532
-
Views remain thin. They arrange widgets, connect route params, and defer behavior to hooks.
537
+
Views remain thin. They arrange widgets, connect route params, and handle cross-cutting concerns (eligibility guards, imperative actions). Views do not fetch data directly — widgets handle that internally.
533
538
534
539
### PredictHome
535
540
536
541
Composition:
537
542
538
-
-`FeaturedCarousel`
539
-
-`EventFeed`
540
-
-`PortfolioSection`
543
+
-`FeaturedCarousel` (fetches featured events internally)
544
+
-`EventFeed` (fetches event list and search internally)
-`useEventDetail` from `hooks/events` — single event by ID
583
+
-`usePositions` from `hooks/portfolio` — user positions for this event
584
+
-`useLiveData` from `hooks/live-data` — real-time price updates
585
+
-`usePriceHistory` from `hooks/events` — chart data
586
+
587
+
Note: EventDetails is a view that directly renders primitives rather than composing widgets, because its layout is unique and not reusable elsewhere. This is fine — not every view needs to delegate to widgets.
565
588
566
589
Route params:
567
590
@@ -571,12 +594,11 @@ Route params:
571
594
572
595
Composition:
573
596
574
-
-`OrderForm`
597
+
-`OrderForm` (handles trading hooks internally)
575
598
576
599
Hooks wired at view level:
577
600
578
-
-`useTrading`
579
-
-`usePredictGuard`
601
+
-`usePredictGuard` — final eligibility check before order entry
580
602
581
603
Route params:
582
604
@@ -587,12 +609,11 @@ Route params:
587
609
588
610
Composition:
589
611
590
-
-`ActivityList`
612
+
-`ActivityList` (fetches activity internally)
591
613
592
614
Hooks wired at view level:
593
615
594
-
-`usePortfolio`
595
-
-`useTransactions`
616
+
-`useTransactions` from `hooks/transactions` — pending transaction state
**Primitives** are pure render components. They receive domain entities via props and render them. No hooks, no side effects, no data fetching. This is what makes them reusable across feeds, detail screens, and external embed points.
632
+
633
+
**Widgets** are the integration layer between data and presentation. An `EventFeed` calls `useEventList` and `useEventSearch` internally, then renders `EventCard` primitives. A `PortfolioSection` calls `usePositions`, `useBalance`, and `usePnL`, then renders `PositionCard` and `PriceDisplay` primitives. Widgets own the data wiring so views stay thin.
634
+
635
+
**Views** compose widgets and handle cross-cutting concerns: route params, eligibility guards, imperative actions (trading, transactions). A view like `PredictHome` mostly arranges widgets — it does not fetch event lists or positions directly.
636
+
637
+
This split means:
638
+
639
+
- Changing how events are fetched only touches widget code, not view or primitive code.
640
+
- Primitives can be tested with plain props (no mock hooks needed).
641
+
- Views are easy to test with the component view framework since they mostly compose widgets.
642
+
621
643
## Hook Composition Rules
622
644
623
-
1. Deep hooks compose services, not each other.
624
-
2. Views compose deep hooks and thin local hooks.
625
-
3. Components do not import services directly.
626
-
4. Query hooks use stable query keys and avoid inline cache semantics.
627
-
5. Imperative hooks return a small state machine instead of leaking service internals.
628
-
6. Error translation happens in services or deep hooks, never in presentational components.
645
+
1. Imperative hooks compose services, not each other.
646
+
2. Widgets compose data query hooks with primitives.
647
+
3. Views compose widgets and imperative/guard hooks.
648
+
4. Primitives never use hooks — data arrives via props.
649
+
5. No tier imports services directly — always go through hooks.
650
+
6. Query hooks use stable query keys and avoid inline cache semantics.
651
+
7. Imperative hooks return a small state machine instead of leaking service internals.
652
+
8. Error translation happens in services or imperative hooks, never in primitives.
0 commit comments