All skills
asyrafhussin avatar

/typescript-react-patterns

@e60e907

TypeScript best practices for React development. Use when writing typed React components, hooks, events, refs, or generic components. Triggers on tasks involving TypeScript errors, type definitions, props typing, or type-safe React patterns.

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/typescript-react-patterns

This session only. Nothing lands on disk.

ruleshook-useref.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

useRef Hook Typing

Impact: CRITICAL (prevents null reference errors and unnecessary null checks)

useRef has two distinct use cases with different typing: DOM element refs (nullable) and mutable value storage (non-nullable). Using the wrong pattern causes type errors or requires unnecessary null checks.

Incorrect

// ❌ Bad
// Missing element type
const inputRef = useRef(null)
inputRef.current.focus()  // Error: possibly null

// Wrong initial value for DOM ref
const inputRef = useRef<HTMLInputElement>()  // undefined, not null
<input ref={inputRef} />  // Type error

// Treating mutable ref as nullable
const countRef = useRef<number>(0)
if (countRef.current !== null) {  // Unnecessary check
  countRef.current++
}

Problems:

  • Missing element type generic means current has no useful properties
  • Using undefined instead of null for DOM refs causes type incompatibility
  • Unnecessary null checks on mutable refs add noise and reduce readability

Correct

// ✅ Good
// DOM Element Refs - pass null, type the element
// const inputRef = useRef<HTMLInputElement>(null)
// const buttonRef = useRef<HTMLButtonElement>(null)
// const divRef = useRef<HTMLDivElement>(null)

function Form() {
  const inputRef = useRef<HTMLInputElement>(null)

  const focusInput = () => {
    // Optional chaining because ref might not be attached yet
    inputRef.current?.focus()
  }

  useEffect(() => {
    inputRef.current?.focus()
  }, [])

  return <input ref={inputRef} />
}

// Common DOM Element Types (standalone declarations for reference)
// const inputRef = useRef<HTMLInputElement>(null)
// const textareaRef = useRef<HTMLTextAreaElement>(null)
// const selectRef = useRef<HTMLSelectElement>(null)
// const formRef = useRef<HTMLFormElement>(null)
// const divRef = useRef<HTMLDivElement>(null)
// const videoRef = useRef<HTMLVideoElement>(null)
// const canvasRef = useRef<HTMLCanvasElement>(null)
// const svgRef = useRef<SVGSVGElement>(null)

// Mutable Value Refs - pass actual initial value
function Timer() {
  const intervalRef = useRef<number | undefined>(undefined)
  const countRef = useRef(0)  // Inferred as MutableRefObject<number>

  useEffect(() => {
    intervalRef.current = window.setInterval(() => {
      countRef.current++  // No null check needed
    }, 1000)

    return () => {
      clearInterval(intervalRef.current)
    }
  }, [])
}

// Storing Previous Value
function usePrevious<T>(value: T): T | undefined {
  const ref = useRef<T | undefined>(undefined)

  useEffect(() => {
    ref.current = value
  }, [value])

  return ref.current
}

function Counter({ count }: { count: number }) {
  const prevCount = usePrevious(count)

  return (
    <p>
      Current: {count}, Previous: {prevCount ?? 'none'}
    </p>
  )
}

// Storing Callbacks
function useEventCallback<T extends (...args: never[]) => unknown>(fn: T): T {
  const ref = useRef<T>(fn)

  useEffect(() => {
    ref.current = fn
  }, [fn])

  return useCallback(
    ((...args) => ref.current(...args)) as T,
    []
  )
}

// Multiple Refs (Callback Refs)
interface Item { id: string; name: string }

function List({ items }: { items: Item[] }) {
  const itemRefs = useRef<Map<string, HTMLLIElement>>(new Map())

  const setRef = (id: string) => (el: HTMLLIElement | null) => {
    if (el) {
      itemRefs.current.set(id, el)
    } else {
      itemRefs.current.delete(id)
    }
  }

  const scrollToItem = (id: string) => {
    itemRefs.current.get(id)?.scrollIntoView()
  }

  return (
    <ul>
      {items.map(item => (
        <li key={item.id} ref={setRef(item.id)}>
          {item.name}
        </li>
      ))}
    </ul>
  )
}

Benefits:

  • DOM refs with null initial value create RefObject<T> with nullable current
  • Mutable refs with actual initial value create MutableRefObject<T> with non-nullable current
  • Optional chaining on DOM refs handles the "not yet attached" state cleanly
  • Callback refs enable dynamic ref management for lists of elements
  • Ref-stored callbacks avoid stale closure issues without triggering re-renders

Reference: React TypeScript Cheatsheet

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill contains educational reference material and code documentation for TypeScript React development patterns. No security risks or malicious vectors were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    30 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at e60e907. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 7 months ago
metadata
{
  "author": "agent-skills",
  "version": "2.0.0"
}

README badge

README badge for asyrafhussin/agent-skills/typescript-react-patterns