Capture two conventions surfaced while building the top bar: use the Relay @required directive to make expected-present nullable fields non-null for consistent typing, and reserve default exports for the component that lazy() imports as a bundle entry while everything else uses named exports. Signed-off-by: Émile Ré <emile@probo.com>
49 lines
2.2 KiB
Plaintext
49 lines
2.2 KiB
Plaintext
---
|
|
description: Use Relay @required to make expected-present nullable fields non-null
|
|
globs: "**/*.tsx"
|
|
alwaysApply: false
|
|
---
|
|
|
|
# Use `@required` for expected-present fields
|
|
|
|
GraphQL schemas mark many fields nullable defensively, but at a given call site
|
|
you usually **expect** a value. When a field is nullable in the schema but the
|
|
component cannot meaningfully render without it, annotate it with `@required` so
|
|
the **generated type is non-null**. This keeps typing consistent: callers stop
|
|
threading `?.` / `?? ""` / `!` through code that always expects data, and a
|
|
genuinely-missing value becomes a real signal instead of a silently-empty UI.
|
|
|
|
Choose the action by what should happen when the value is actually absent:
|
|
|
|
- `@required(action: THROW)` — the value is an invariant for this view (a page's
|
|
root entity, the `currentTrustCenter` a portal is built around). A null throws
|
|
on read and propagates to the nearest error boundary. Field becomes non-null.
|
|
- `@required(action: LOG)` — a missing value should degrade gracefully: the null
|
|
bubbles to the nearest `@required` ancestor (or nulls the fragment data) and
|
|
Relay logs it. Use when the surrounding UI can render a fallback.
|
|
- `@required(action: NONE)` — bubble nullability without logging; rarely needed.
|
|
|
|
```graphql
|
|
# GOOD — the view is built around this entity; THROW makes it non-null
|
|
currentTrustCenter @required(action: THROW) {
|
|
organization {
|
|
name # already String! — no @required needed
|
|
logo { downloadUrl } # legitimately optional — leave nullable
|
|
}
|
|
}
|
|
```
|
|
|
|
```tsx
|
|
// GOOD — non-null typing falls out of @required; no defensive chaining
|
|
const { organization } = data.currentTrustCenter;
|
|
const logoUrl = organization.logo?.downloadUrl ?? undefined; // logo stays optional
|
|
```
|
|
|
|
Do NOT use `@required` to silence nullability on fields that are *genuinely*
|
|
optional (avatar, logo, optional description) — those keep their nullable type
|
|
and get a real empty/fallback state. Do NOT use the `THROW` as control flow for
|
|
an expected-empty case (it is an error path, not a branch). Do NOT annotate
|
|
fields the schema already declares non-null (`String!`, `Organization!`).
|
|
|
|
See `contrib/claude/relay.md` (Fragments → Required fields).
|