Skip to content

rendering-patterns

<!-- Loaded by react-native-engineer when task involves conditional rendering, &&, Text components, React Compiler, memoization -->

From plugin
vexjoy-agent
413198 skills198 agents10 commands86 hooks
Install
$ npx -y skills add notque/vexjoy-agent --agent claude-code

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

<!-- Loaded by react-native-engineer when task involves conditional rendering, &&, Text components, React Compiler, memoization -->

Agent definition

rendering-patterns.md

Rendering Patterns Reference

<!-- Loaded by react-native-engineer when task involves conditional rendering, &&, Text components, React Compiler, memoization -->

Use Ternary or Explicit Boolean for Conditional Rendering

**Impact:** CRITICAL — prevents hard crash in production

`{value && <Component />}` crashes React Native when `value` is `0` or empty string. These are falsy but JSX-renderable — RN tries to render them as text outside `<Text>`, causing a hard crash. React Native-specific (not a web issue).

**Instead of:**

{name && <Text>{name}</Text>}   // crashes if name is ""
{count && <Text>{count} items</Text>}  // crashes if count is 0

**Use:**

{name ? <Text>{name}</Text> : null}
{count > 0 ? <Text>{count} items</Text> : null}
// Or: {!!name && <Text>{name}</Text>}
// Or: early return — if (!name) return null

Enable `react/jsx-no-leaked-render` ESLint rule.

---

Wrap All Strings in Text Components

**Impact:** CRITICAL — prevents runtime crash

Strings must be inside `<Text>`. React Native throws a hard error on string children of `<View>`.

**Instead of:**

return <View>Hello, {name}!</View>

**Use:**

return <View><Text>Hello, {name}!</Text></View>

Applies to string literals, template literals, and expressions that evaluate to strings.

---

Destructure Functions Early in Render (React Compiler)

**Impact:** HIGH — stable references, fewer re-renders

With React Compiler, destructure functions from hooks and props at render scope top. The compiler keys cache on read variables — dotting into objects uses the object reference (changes each render).

**Instead of:**

function SaveButton(props) {
  const router = useRouter()
  const handlePress = () => { props.onSave(); router.push('/success') }
  return <Button onPress={handlePress}>Save</Button>
}

**Use:**

function SaveButton({ onSave }) {
  const { push } = useRouter()
  const handlePress = () => { onSave(); push('/success') }
  return <Button onPress={handlePress}>Save</Button>
}

Only applies with React Compiler enabled. Without it, use standard `useCallback`.

---

Memoization Patterns Without React Compiler

const UserRow = memo(function UserRow({ name, isActive }: Props) {
  return <View>{/* ... */}</View>
})

const handlePress = useCallback((id: string) => {
  dispatch({ type: 'SELECT', id })
}, [dispatch])

const sorted = useMemo(
  () => items.toSorted((a, b) => a.name.localeCompare(b.name)),
  [items]
)

With React Compiler, `memo()` and `useCallback()` are handled automatically. Object reference stability still matters for virtualized lists.

---

Hoist Intl Formatters to Module Scope

**Impact:** LOW-MEDIUM — avoids expensive instantiation per render

**Instead of:**

function Price({ amount }: { amount: number }) {
  const formatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' })
  return <Text>{formatter.format(amount)}</Text>
}

**Use:**

const currencyFormatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' })

function Price({ amount }: { amount: number }) {
  return <Text>{currencyFormatter.format(amount)}</Text>
}

For dynamic locales, memoize: `useMemo(() => new Intl.DateTimeFormat(locale, opts), [locale])`.

Read more
Ships withvexjoy-agent

Essays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.

Get the whole plugin, auto-invoked