In React components and hooks, changes to focus/interaction state or derived UI state must preserve correct behavior for all users and reflect the actual semantic state transitions.
Apply this standard: 1) Interactive controls: ARIA + roving focus requires keyboard support
tabIndex (only the active tab is focusable), you must also implement expected keyboard navigation (typically ArrowLeft/ArrowRight/Home/End) to move focus/selection among tabs.Example (tablist pattern):
function onTabKeyDown(e: React.KeyboardEvent, tabs: Tab[], currentId: string) {
const idx = tabs.findIndex(t => t.id === currentId)
const move = (nextIdx: number) => {
const next = tabs[(nextIdx + tabs.length) % tabs.length]
selectTab(next.id) // set active + update tabIndex
focusTabButton(next.id) // optional: focus the newly selected tab button
}
switch (e.key) {
case 'ArrowLeft': e.preventDefault(); move(idx - 1); break
case 'ArrowRight': e.preventDefault(); move(idx + 1); break
case 'Home': e.preventDefault(); move(0); break
case 'End': e.preventDefault(); move(tabs.length - 1); break
}
}
2) Hooks/effects: rerun on the semantic inputs, not just “session changes”
useEffect dependency lists include the values that should trigger the behavior (e.g., language/locale).Example (separate locale-independent selection from locale-dependent text):
type PlaceholderSelection = { kind: 'new'|'followUp'; index: number }
function getPlaceholderText(sel: PlaceholderSelection, locale: string) {
const templates = sel.kind === 'new' ? newTemplatesByLocale[locale] : followUpTemplatesByLocale[locale]
return templates[sel.index]
}
// Keep sel stable; derive text from locale
const [sel, setSel] = useState<PlaceholderSelection>(() => ({ kind, index }))
const text = getPlaceholderText(sel, locale)
Net effect: keyboard-only users can operate interactive tab UI reliably, and hooks update derived text/state correctly when language or other semantic inputs change—without unintended rerolls or history resets.
Enter the URL of a public GitHub repository