react-ui-patterns

安装量: 618
排名: #1835

安装

npx skills add https://github.com/sickn33/antigravity-awesome-skills --skill react-ui-patterns
React UI Patterns
Core Principles
Never show stale UI
- Loading spinners only when actually loading
Always surface errors
- Users must know when something fails
Optimistic updates
- Make the UI feel instant
Progressive disclosure
- Show content as it becomes available
Graceful degradation
- Partial data is better than no data
Loading State Patterns
The Golden Rule
Show loading indicator ONLY when there's no data to display.
// CORRECT - Only show loading when no data exists
const
{
data
,
loading
,
error
}
=
useGetItemsQuery
(
)
;
if
(
error
)
return
<
ErrorState error
=
{
error
}
onRetry
=
{
refetch
}
/
>
;
if
(
loading
&&
!
data
)
return
<
LoadingState
/
>
;
if
(
!
data
?.
items
.
length
)
return
<
EmptyState
/
>
;
return
<
ItemList items
=
{
data
.
items
}
/
>
;
// WRONG - Shows spinner even when we have cached data
if
(
loading
)
return
<
LoadingState
/
>
;
// Flashes on refetch!
Loading State Decision Tree
Is there an error?
→ Yes: Show error state with retry option
→ No: Continue
Is it loading AND we have no data?
→ Yes: Show loading indicator (spinner/skeleton)
→ No: Continue
Do we have data?
→ Yes, with items: Show the data
→ Yes, but empty: Show empty state
→ No: Show loading (fallback)
Skeleton vs Spinner
Use Skeleton When
Use Spinner When
Known content shape
Unknown content shape
List/card layouts
Modal actions
Initial page load
Button submissions
Content placeholders
Inline operations
Error Handling Patterns
The Error Handling Hierarchy
1. Inline error (field-level) → Form validation errors
2. Toast notification → Recoverable errors, user can retry
3. Error banner → Page-level errors, data still partially usable
4. Full error screen → Unrecoverable, needs user action
Always Show Errors
CRITICAL: Never swallow errors silently.
// CORRECT - Error always surfaced to user
const
[
createItem
,
{
loading
}
]
=
useCreateItemMutation
(
{
onCompleted
:
(
)
=>
{
toast
.
success
(
{
title
:
'Item created'
}
)
;
}
,
onError
:
(
error
)
=>
{
console
.
error
(
'createItem failed:'
,
error
)
;
toast
.
error
(
{
title
:
'Failed to create item'
}
)
;
}
,
}
)
;
// WRONG - Error silently caught, user has no idea
const
[
createItem
]
=
useCreateItemMutation
(
{
onError
:
(
error
)
=>
{
console
.
error
(
error
)
;
// User sees nothing!
}
,
}
)
;
Error State Component Pattern
interface
ErrorStateProps
{
error
:
Error
;
onRetry
?
:
(
)
=>
void
;
title
?
:
string
;
}
const
ErrorState
=
(
{
error
,
onRetry
,
title
}
:
ErrorStateProps
)
=>
(
<
div className
=
"error-state"
>
<
Icon name
=
"exclamation-circle"
/
>
<
h3
>
{
title
??
'Something went wrong'
}
<
/
h3
>
<
p
>
{
error
.
message
}
<
/
p
>
{
onRetry
&&
(
<
Button onClick
=
{
onRetry
}
>
Try Again
<
/
Button
>
)
}
<
/
div
>
)
;
Button State Patterns
Button Loading State
<
Button
onClick
=
{
handleSubmit
}
isLoading
=
{
isSubmitting
}
disabled
=
{
!
isValid
||
isSubmitting
}
>
Submit
</
Button
>
Disable During Operations
CRITICAL: Always disable triggers during async operations.
// CORRECT - Button disabled while loading
<
Button
disabled
=
{
isSubmitting
}
isLoading
=
{
isSubmitting
}
onClick
=
{
handleSubmit
}
>
Submit
</
Button
>
// WRONG - User can tap multiple times
<
Button
onClick
=
{
handleSubmit
}
>
{
isSubmitting
?
'Submitting...'
:
'Submit'
}
</
Button
>
Empty States
Empty State Requirements
Every list/collection MUST have an empty state:
// WRONG - No empty state
return
<
FlatList
data
=
{
items
}
/>
;
// CORRECT - Explicit empty state
return
(
<
FlatList
data
=
{
items
}
ListEmptyComponent
=
{
<
EmptyState
/>
}
/>
)
;
Contextual Empty States
// Search with no results
<
EmptyState
icon
=
"
search
"
title
=
"
No results found
"
description
=
"
Try different search terms
"
/>
// List with no items yet
<
EmptyState
icon
=
"
plus-circle
"
title
=
"
No items yet
"
description
=
"
Create your first item
"
action
=
{
{
label
:
'Create Item'
,
onClick
:
handleCreate
}
}
/>
Form Submission Pattern
const
MyForm
=
(
)
=>
{
const
[
submit
,
{
loading
}
]
=
useSubmitMutation
(
{
onCompleted
:
handleSuccess
,
onError
:
handleError
,
}
)
;
const
handleSubmit
=
async
(
)
=>
{
if
(
!
isValid
)
{
toast
.
error
(
{
title
:
'Please fix errors'
}
)
;
return
;
}
await
submit
(
{
variables
:
{
input
:
values
}
}
)
;
}
;
return
(
<
form
>
<
Input
value
=
{
values
.
name
}
onChange
=
{
handleChange
(
'name'
)
}
error
=
{
touched
.
name
?
errors
.
name
:
undefined
}
/>
<
Button
type
=
"
submit
"
onClick
=
{
handleSubmit
}
disabled
=
{
!
isValid
||
loading
}
isLoading
=
{
loading
}
>
Submit
</
Button
>
</
form
>
)
;
}
;
Anti-Patterns
Loading States
// WRONG - Spinner when data exists (causes flash)
if
(
loading
)
return
<
Spinner
/
>
;
// CORRECT - Only show loading without data
if
(
loading
&&
!
data
)
return
<
Spinner
/
>
;
Error Handling
// WRONG - Error swallowed
try
{
await
mutation
(
)
;
}
catch
(
e
)
{
console
.
log
(
e
)
;
// User has no idea!
}
// CORRECT - Error surfaced
onError
:
(
error
)
=>
{
console
.
error
(
'operation failed:'
,
error
)
;
toast
.
error
(
{
title
:
'Operation failed'
}
)
;
}
Button States
// WRONG - Button not disabled during submission
<
Button onClick
=
{
submit
}
>
Submit
<
/
Button
>
// CORRECT - Disabled and shows loading
<
Button onClick
=
{
submit
}
disabled
=
{
loading
}
isLoading
=
{
loading
}
>
Submit
<
/
Button
>
Checklist
Before completing any UI component:
UI States:
Error state handled and shown to user
Loading state shown only when no data exists
Empty state provided for collections
Buttons disabled during async operations
Buttons show loading indicator when appropriate
Data & Mutations:
Mutations have onError handler
All user actions have feedback (toast/visual)
Integration with Other Skills
graphql-schema
Use mutation patterns with proper error handling
testing-patterns
Test all UI states (loading, error, empty, success)
formik-patterns
Apply form submission patterns When to Use This skill is applicable to execute the workflow or actions described in the overview.
返回排行榜