Skip to content

Commit 2b6264f

Browse files
committed
docs: clarify hook usage per component tier
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.
1 parent 928aa5e commit 2b6264f

File tree

2 files changed

+84
-39
lines changed

2 files changed

+84
-39
lines changed

app/components/UI/PredictNext/docs/components.md

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Core rules:
1717
- Use the MetaMask Mobile design system first: `useTailwind`, `Box`, and `Text`
1818
- Use compound components where a parent can provide shared context to related children
1919
- Keep domain formatting and rendering logic inside primitives when it improves reuse
20+
- Primitives are pure (no hooks) — widgets wire data hooks to primitives — views compose widgets
2021

2122
Related docs:
2223

@@ -412,7 +413,9 @@ export function Skeleton({ layout }: SkeletonProps) {
412413

413414
## Tier 2: Composed Widgets
414415

415-
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.
416419

417420
### EventFeed
418421

@@ -425,16 +428,17 @@ Composes:
425428
- `EventCard`
426429
- `Skeleton`
427430

428-
Hooks:
431+
Hooks (called internally by the widget):
429432

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
432436

433437
Typical responsibilities:
434438

435439
- search box input
436440
- category tab state
437-
- pagination trigger
441+
- pagination trigger via `fetchMore`
438442
- empty and loading states
439443

440444
### FeaturedCarousel
@@ -447,10 +451,10 @@ Composes:
447451

448452
- `EventCard`
449453

450-
Hooks:
454+
Hooks (called internally by the widget):
451455

452-
- `useEvents` for `featured`
453-
- `usePredictNavigation`
456+
- `useFeaturedEvents` from `hooks/events` — carousel events
457+
- `usePredictNavigation` from `hooks/navigation` — tap-to-details navigation
454458

455459
Typical responsibilities:
456460

@@ -470,10 +474,11 @@ Composes:
470474
- `PriceDisplay`
471475
- `Skeleton`
472476

473-
Hooks:
477+
Hooks (called internally by the widget):
474478

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
477482

478483
Typical responsibilities:
479484

@@ -492,11 +497,11 @@ Composes:
492497
- `OutcomeButton`
493498
- `PriceDisplay`
494499

495-
Hooks:
500+
Hooks (called internally by the widget):
496501

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
500505

501506
Typical responsibilities:
502507

@@ -516,10 +521,10 @@ Composes:
516521
- `PriceDisplay`
517522
- `Skeleton`
518523

519-
Hooks:
524+
Hooks (called internally by the widget):
520525

521-
- `usePortfolio`
522-
- `usePredictNavigation`
526+
- `useActivity` from `hooks/portfolio` — transaction history
527+
- `usePredictNavigation` from `hooks/navigation` — detail sheet navigation
523528

524529
Typical responsibilities:
525530

@@ -529,25 +534,40 @@ Typical responsibilities:
529534

530535
## Tier 3: Views
531536

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.
533538

534539
### PredictHome
535540

536541
Composition:
537542

538-
- `FeaturedCarousel`
539-
- `EventFeed`
540-
- `PortfolioSection`
543+
- `FeaturedCarousel` (fetches featured events internally)
544+
- `EventFeed` (fetches event list and search internally)
545+
- `PortfolioSection` (fetches positions, balance, P&L internally)
541546

542547
Hooks wired at view level:
543548

544-
- `usePredictGuard`
545-
- `usePredictNavigation`
549+
- `usePredictGuard` — gate access for geo-blocked or ineligible users
550+
- `usePredictNavigation` — tab state, scroll management
546551

547552
Route params:
548553

549554
- none required
550555

556+
```tsx
557+
export function PredictHome() {
558+
const { isEligible } = usePredictGuard();
559+
if (!isEligible) return <UnavailableModal />;
560+
561+
return (
562+
<ScrollView>
563+
<FeaturedCarousel />
564+
<EventFeed />
565+
<PortfolioSection />
566+
</ScrollView>
567+
);
568+
}
569+
```
570+
551571
### EventDetails
552572

553573
Composition:
@@ -559,9 +579,12 @@ Composition:
559579

560580
Hooks wired at view level:
561581

562-
- `useEvents`
563-
- `usePortfolio`
564-
- `useLiveData`
582+
- `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.
565588

566589
Route params:
567590

@@ -571,12 +594,11 @@ Route params:
571594

572595
Composition:
573596

574-
- `OrderForm`
597+
- `OrderForm` (handles trading hooks internally)
575598

576599
Hooks wired at view level:
577600

578-
- `useTrading`
579-
- `usePredictGuard`
601+
- `usePredictGuard` — final eligibility check before order entry
580602

581603
Route params:
582604

@@ -587,12 +609,11 @@ Route params:
587609

588610
Composition:
589611

590-
- `ActivityList`
612+
- `ActivityList` (fetches activity internally)
591613

592614
Hooks wired at view level:
593615

594-
- `usePortfolio`
595-
- `useTransactions`
616+
- `useTransactions` from `hooks/transactions` — pending transaction state
596617

597618
Route params:
598619

app/components/UI/PredictNext/docs/hooks.md

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -618,14 +618,38 @@ export function useBuyViewState({
618618

619619
This pattern keeps deep hooks stable and reusable while allowing view code to stay explicit.
620620

621+
## Hook Usage by Component Tier
622+
623+
Not every tier uses hooks. The rule is: primitives are pure, widgets wire data, views orchestrate.
624+
625+
| Tier | Uses hooks? | Uses services directly? | Receives props? |
626+
| ------------------------------------------- | ------------------------------ | ----------------------- | ------------------------------ |
627+
| Primitives (EventCard, OutcomeButton, etc.) | No | No | Yes — data + callbacks |
628+
| Widgets (EventFeed, PortfolioSection, etc.) | Yes — data query hooks | No | Yes — config/params from views |
629+
| Views (PredictHome, EventDetails, etc.) | Yes — imperative + guard hooks | No | Yes — route params |
630+
631+
**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+
621643
## Hook Composition Rules
622644

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.
629653

630654
## Example View Composition
631655

0 commit comments

Comments
 (0)