11 KiB
React 19 Features Guide
Overview
This guide covers the new React 19 features available in our codebase and provides practical examples for implementing them.
1. New Hooks for Forms
useFormStatus - Track Form Submission State
What it does: Provides access to the current form's submission status without manual state management.
Use Case: Show loading states on submit buttons, disable inputs during submission.
Example:
import { useFormStatus } from 'react';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Saving...' : 'Save'}
</button>
);
}
function JobForm({ onSave }) {
return (
<form action={onSave}>
<input name="jobNumber" />
<SubmitButton />
</form>
);
}
Benefits:
- No manual
useStatefor loading states - Automatic re-renders when form status changes
- Better separation of concerns (button doesn't need form state)
useOptimistic - Instant UI Updates
What it does: Updates UI immediately while async operations complete in the background.
Use Case: Comments, notes, status updates - anything where you want instant feedback.
Example:
import { useState, useOptimistic } from 'react';
function JobNotes({ jobId, initialNotes }) {
const [notes, setNotes] = useState(initialNotes);
const [optimisticNotes, addOptimisticNote] = useOptimistic(
notes,
(current, newNote) => [...current, newNote]
);
async function handleAddNote(formData) {
const text = formData.get('note');
const tempNote = { id: Date.now(), text, pending: true };
// Show immediately
addOptimisticNote(tempNote);
// Save to server
const saved = await saveNote(jobId, text);
setNotes([...notes, saved]);
}
return (
<form action={handleAddNote}>
<textarea name="note" />
<button type="submit">Add Note</button>
<ul>
{optimisticNotes.map(note => (
<li key={note.id} style={{ opacity: note.pending ? 0.5 : 1 }}>
{note.text}
</li>
))}
</ul>
</form>
);
}
Benefits:
- Perceived performance improvement
- Better UX - users see changes instantly
- Automatic rollback on error (if implemented)
useActionState - Complete Form State Management
What it does: Manages async form submissions with built-in loading, error, and success states.
Use Case: Form validation, API submissions, complex form workflows.
Example:
import { useActionState } from 'react';
async function createContract(prevState, formData) {
const data = {
customerId: formData.get('customerId'),
vehicleId: formData.get('vehicleId'),
};
try {
const result = await fetch('/api/contracts', {
method: 'POST',
body: JSON.stringify(data),
});
if (!result.ok) {
return { error: 'Failed to create contract', data: null };
}
return { error: null, data: await result.json() };
} catch (err) {
return { error: err.message, data: null };
}
}
function ContractForm() {
const [state, submitAction, isPending] = useActionState(
createContract,
{ error: null, data: null }
);
return (
<form action={submitAction}>
<input name="customerId" required />
<input name="vehicleId" required />
<button type="submit" disabled={isPending}>
{isPending ? 'Creating...' : 'Create Contract'}
</button>
{state.error && <div className="error">{state.error}</div>}
{state.data && <div className="success">Contract #{state.data.id} created!</div>}
</form>
);
}
Benefits:
- Replaces multiple
useStatecalls - Built-in pending state
- Cleaner error handling
- Type-safe with TypeScript
2. Actions API
The Actions API simplifies form submissions and async operations by using the native action prop on forms.
Traditional Approach (React 18):
function OldForm() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
async function handleSubmit(e) {
e.preventDefault();
setLoading(true);
setError(null);
try {
const formData = new FormData(e.target);
await saveData(formData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
return (
<form onSubmit={handleSubmit}>
{/* form fields */}
</form>
);
}
Modern Approach (React 19):
import { useActionState } from 'react';
function NewForm() {
const [state, formAction, isPending] = useActionState(async (_, formData) => {
return await saveData(formData);
}, null);
return (
<form action={formAction}>
{/* form fields */}
</form>
);
}
3. Practical Implementation Examples
Example 1: Owner/Customer Form with Optimistic UI
import { useOptimistic, useActionState } from 'react';
import { Form, Input, Button } from 'antd';
function OwnerFormModern({ owner, onSave }) {
const [optimisticOwner, setOptimisticOwner] = useOptimistic(
owner,
(current, updates) => ({ ...current, ...updates })
);
const [state, submitAction, isPending] = useActionState(
async (_, formData) => {
const updates = {
name: formData.get('name'),
phone: formData.get('phone'),
email: formData.get('email'),
};
// Show changes immediately
setOptimisticOwner(updates);
// Save to server
try {
await onSave(updates);
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
},
{ success: null }
);
return (
<form action={submitAction}>
<Form.Item label="Name">
<Input name="name" defaultValue={optimisticOwner.name} />
</Form.Item>
<Form.Item label="Phone">
<Input name="phone" defaultValue={optimisticOwner.phone} />
</Form.Item>
<Form.Item label="Email">
<Input name="email" defaultValue={optimisticOwner.email} />
</Form.Item>
<Button type="primary" htmlType="submit" loading={isPending}>
{isPending ? 'Saving...' : 'Save Owner'}
</Button>
{state.error && <div className="error">{state.error}</div>}
</form>
);
}
Example 2: Job Status Update with useFormStatus
import { useFormStatus } from 'react';
function JobStatusButton({ status }) {
const { pending } = useFormStatus();
return (
<button disabled={pending}>
{pending ? 'Updating...' : `Mark as ${status}`}
</button>
);
}
function JobStatusForm({ jobId, currentStatus }) {
async function updateStatus(formData) {
const newStatus = formData.get('status');
await fetch(`/api/jobs/${jobId}/status`, {
method: 'PATCH',
body: JSON.stringify({ status: newStatus }),
});
}
return (
<form action={updateStatus}>
<input type="hidden" name="status" value="IN_PROGRESS" />
<JobStatusButton status="In Progress" />
</form>
);
}
4. Third-Party Library Compatibility
✅ Fully Compatible (Already in use)
-
Ant Design 6.2.0
- ✅ Full React 19 support out of the box
- ✅ No patches or workarounds needed
- 📝 Note: Ant Design 6 was built with React 19 in mind
-
React-Redux 9.2.0
- ✅ Full React 19 support
- ✅ All hooks (
useSelector,useDispatch) work correctly - 📝 Tip: Continue using hooks over
connect()HOC
-
Apollo Client 4.0.13
- ✅ Compatible with React 19
- ✅
useQuery,useMutationwork correctly - 📝 Note: Supports React 19's concurrent features
-
React Router 7.12.0
- ✅ Full React 19 support
- ✅ All navigation hooks compatible
- ✅ Future flags enabled for optimal performance
Integration Notes
All our major dependencies are already compatible with React 19:
- No additional patches needed
- No breaking changes in current code
- All hooks and patterns continue to work
5. Migration Strategy
Gradual Adoption Approach
Phase 1: Learn (Current)
- Review this guide
- Understand new hooks and patterns
- Identify good candidates for migration
Phase 2: Pilot (Recommended)
- Start with new features/forms
- Try
useActionStatein one new form - Measure developer experience improvement
Phase 3: Refactor (Optional)
- Gradually update high-traffic forms
- Add optimistic UI to user-facing features
- Simplify complex form state management
Good Candidates for React 19 Features
-
Forms with Complex Loading States
- Contract creation
- Job creation/editing
- Owner/Vehicle forms
- → Use
useActionState
-
Instant Feedback Features
- Adding job notes
- Status updates
- Comments/messages
- → Use
useOptimistic
-
Submit Buttons
- Any form button that needs loading state
- → Use
useFormStatus
Don't Rush to Refactor
Keep using current patterns for:
- Ant Design Form components (already excellent)
- Redux for global state
- Apollo Client for GraphQL
- Existing working code
Only refactor when:
- Building new features
- Fixing bugs in forms
- Simplifying overly complex state management
6. Performance Improvements in React 19
Automatic Optimizations
React 19 includes built-in compiler optimizations that automatically improve performance:
-
Automatic Memoization
- Less need for
useMemoanduseCallback - Components automatically optimize re-renders
- Less need for
-
Improved Concurrent Rendering
- Better handling of heavy operations
- Smoother UI during data loading
-
Enhanced Suspense
- Better loading states
- Improved streaming SSR
What this means for us:
- Existing code may run faster without changes
- Future code will be easier to write
- Less manual optimization needed
7. Resources
Official Documentation
Migration Guides
Community Resources
8. Summary
Current Status
✅ All dependencies compatible with React 19
- Ant Design 6.2.0 ✓
- React-Redux 9.2.0 ✓
- Apollo Client 4.0.13 ✓
- React Router 7.12.0 ✓
New Features Available
🎯 Ready to use in new code:
useFormStatus- Track form submission stateuseOptimistic- Instant UI updatesuseActionState- Complete form state management- Actions API - Cleaner form handling
Recommendations
- ✅ No immediate action required - Everything works
- 🎯 Start using new features in new code - Especially forms
- 📚 Learn gradually - No need to refactor everything
- 🚀 Enjoy performance improvements - Automatic optimizations active
Questions or Need Help?
Feel free to:
- Try examples in a branch first
- Ask the team for code reviews
- Share patterns that work well
- Document new patterns you discover
Happy coding with React 19! 🎉