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.
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(), }); };
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} />; };
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> ); };
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'] }); }, }); };
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); } }
Consider these improvements for your implementation:
Once Completed Request
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:
What challenges have you faced implementing optimistic updates in your React applications? Share your experiences in the comments below.
The above is the detailed content of Building a Data Table with Optimistic Updates. For more information, please follow other related articles on the PHP Chinese website!