This content originally appeared on DEV Community and was authored by Enyel Sequeira
Introduction
Today, I'll share how I built a polished food database management system using modern React patterns. We'll focus on creating a responsive data table with seamless optimistic updates, combining the power of TanStack Query (formerly React Query) with Mantine's component library.
Project Overview
Requirements
- Display food items in a data table
- Add new items with immediate feedback
- Handle loading and error states gracefully
- Provide smooth optimistic updates
Tech Stack
- TanStack Query: Server state management
- Mantine UI: Component library and form management
- Mantine React Table: Advanced table functionality
- Wretch: Clean API calls
- TypeScript: Type safety
Implementation Guide
1. Setting Up the Foundation
First, let's define our types and API configuration:
// Types
export type GetAllFoods = {
id: number;
name: string;
category: string;
};
export type CreateNewFoodType = Pick<
GetAllFoods,
| 'name'
| 'category'
>;
// API Configuration
export const API = wretch('<http://localhost:9999>').options({
credentials: 'include',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
// TANSTACK QUERY
export const getFoodOptions = () => {
return queryOptions({
queryKey: ['all-foods'],
queryFn: async () => {
try {
return await API.get('/foods')
.unauthorized(() => {
console.log('Unauthorized');
})
.json<Array<GetAllFoods>>();
} catch (e) {
console.log({ e });
throw e;
}
},
});
};
export const useGetAllFoods = () => {
return useQuery({
...getFoodOptions(),
});
};
2. Building the Data Table
The table component using Mantine React Table:
const FoodsView = () => {
const { data } = useGetAllFoods();
const columns = useMemo<MRT_ColumnDef<GetAllFoods>[]>(
() => [
{
accessorKey: 'id',
header: 'ID',
},
{
accessorKey: 'name',
header: 'Name',
},
{
accessorKey: 'category',
header: 'Category',
},
// ... other columns
],
[]
);
const table = useMantineReactTable({
columns,
data: data ?? [],
// Optimistic update animation
mantineTableBodyCellProps: ({ row }) => ({
style: row.original.id < 0 ? {
animation: 'shimmer-and-pulse 2s infinite',
background: `linear-gradient(
110deg,
transparent 33%,
rgba(83, 109, 254, 0.2) 50%,
transparent 67%
)`,
backgroundSize: '200% 100%',
position: 'relative',
} : undefined,
}),
});
return <MantineReactTable table={table} />;
};
3. Creating the Form
A form component for adding new foods:
const CreateNewFood = () => {
const { mutate } = useCreateNewFood();
const formInputs = [
{ name: 'name', type: 'text' },
{ name: 'category', type: 'text' },
];
const form = useForm<CreateNewFoodType>({
initialValues: {
name: '',
category: '',
// ... other fields
},
});
return (
<Box mt="md">
<form onSubmit={form.onSubmit((data) => mutate(data))}>
<Flex direction="column" gap="xs">
{formInputs.map((input) => (
<TextInput
key={input.name}
{...form.getInputProps(input.name)}
label={input.name}
tt="uppercase"
type={input.type}
/>
))}
<Button type="submit" mt="md">
Create New
</Button>
</Flex>
</form>
</Box>
);
};
4. Implementing Optimistic Updates
The heart of our implementation - TanStack Query mutation with optimistic updates:
export const useCreateNewFood = () => {
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['create-new-food'],
mutationFn: async (data: CreateNewFoodType) => {
await new Promise(resolve => setTimeout(resolve, 3000)); // Demo delay
return API.url('/foods').post(data).json<GetAllFoods>();
},
onMutate: async (newFood) => {
// Cancel in-flight queries
await queryClient.cancelQueries({ queryKey: ['all-foods'] });
// Snapshot current state
const previousFoods = queryClient.getQueryData<GetAllFoods[]>(['all-foods']);
// Create optimistic entry
const optimisticFood: GetAllFoods = {
id: -Math.random(),
...newFood,
verified: false,
createdBy: 0,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
// Update cache optimistically
queryClient.setQueryData(['all-foods'], (old) =>
old ? [...old, optimisticFood] : [optimisticFood]
);
return { previousFoods };
},
onError: (err, _, context) => {
// Rollback on error
if (context?.previousFoods) {
queryClient.setQueryData(['all-foods'], context.previousFoods);
}
},
onSettled: () => {
// Refetch to ensure consistency
queryClient.invalidateQueries({ queryKey: ['all-foods'] });
},
});
};
5. Animation Styles
The animation that brings our optimistic updates to life:
@keyframes shimmer-and-pulse {
0% {
background-position: 200% 0;
transform: scale(1);
box-shadow: 0 0 0 0 rgba(83, 109, 254, 0.2);
}
50% {
background-position: -200% 0;
transform: scale(1.02);
box-shadow: 0 0 0 10px rgba(83, 109, 254, 0);
}
100% {
background-position: 200% 0;
transform: scale(1);
box-shadow: 0 0 0 0 rgba(83, 109, 254, 0);
}
}
Best Practices
-
Optimistic Updates
- Immediately update UI for better UX
- Handle error cases with rollbacks
- Maintain data consistency with proper invalidation
-
Type Safety
- Use TypeScript for better maintainability
- Define clear interfaces for data structures
- Leverage type inference where possible
-
Performance
- Cancel in-flight queries during updates
- Use proper query invalidation
- Implement efficient form state management
-
User Experience
- Provide immediate feedback
- Show loading states
- Handle errors gracefully
Future Enhancements
Consider these improvements for your implementation:
- Undo/redo functionality
- Form validation rules
- Error boundary implementation
Results
Once Completed Request
Conclusion
This implementation demonstrates how to create a robust data management system using modern React patterns. The combination of TanStack Query, Mantine UI, and thoughtful optimistic updates creates a smooth and professional user experience.
Remember to:
- Keep your components focused and maintainable
- Handle all possible states (loading, error, success)
- Use TypeScript for better code quality
- Consider user experience in your implementation
What challenges have you faced implementing optimistic updates in your React applications? Share your experiences in the comments below.
This content originally appeared on DEV Community and was authored by Enyel Sequeira
![](https://www.radiofree.org/wp-content/plugins/print-app/icon.jpg)
Enyel Sequeira | Sciencx (2024-11-03T16:36:25+00:00) Building a Data Table with Optimistic Updates. Retrieved from https://www.scien.cx/2024/11/03/building-a-data-table-with-optimistic-updates/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.