Maison > interface Web > js tutoriel > Construire une table de données avec des mises à jour optimistes

Construire une table de données avec des mises à jour optimistes

DDD
Libérer: 2024-11-04 07:28:01
original
1027 Les gens l'ont consulté

Introduction

Aujourd'hui, je vais vous expliquer comment j'ai construit un système de gestion de base de données alimentaire raffiné à l'aide de modèles React modernes. Nous nous concentrerons sur la création d'une table de données réactive avec des mises à jour optimistes transparentes, combinant la puissance de TanStack Query (anciennement React Query) avec la bibliothèque de composants de Mantine.

Aperçu du projet

Exigences

  • Afficher les aliments dans un tableau de données
  • Ajoutez de nouveaux éléments avec un retour immédiat
  • Gérer les états de chargement et d'erreur avec élégance
  • Fournir des mises à jour optimistes et fluides

Pile technologique

  • Requête TanStack : Gestion de l'état du serveur
  • Mantine UI : Bibliothèque de composants et gestion des formulaires
  • Mantine React Table : Fonctionnalité de table avancée
  • Wretch : Appels API propres
  • TypeScript : Sécurité des types

Guide de mise en œuvre

1. Mise en place de la Fondation

Tout d'abord, définissons nos types et la configuration de notre API :

// 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(),
  });
};

Copier après la connexion

2. Construire le tableau de données

Le composant table utilisant 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} />;
};

Copier après la connexion

3. Création du formulaire

Un composant de formulaire pour ajouter de nouveaux aliments :

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>
  );
};

Copier après la connexion

4. Implémentation de mises à jour optimistes

Le cœur de notre implémentation - Mutation TanStack Query avec mises à jour optimistes :

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'] });
    },
  });
};

Copier après la connexion

5. Styles d'animations

L'animation qui donne vie à nos mises à jour optimistes :

@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);
  }
}

Copier après la connexion

Meilleures pratiques

  1. Mises à jour optimistes
    • Mettre immédiatement à jour l'interface utilisateur pour une meilleure UX
    • Gérer les cas d'erreur avec les restaurations
    • Maintenir la cohérence des données avec une invalidation appropriée
  2. Type Sécurité
    • Utilisez TypeScript pour une meilleure maintenabilité
    • Définir des interfaces claires pour les structures de données
    • Tirer parti de l'inférence de type lorsque cela est possible
  3. Performances
    • Annuler les requêtes en cours pendant les mises à jour
    • Utiliser l'invalidation de requête appropriée
    • Mettre en œuvre une gestion efficace de l'état des formulaires
  4. Expérience utilisateur
    • Fournir des commentaires immédiats
    • Afficher les états de chargement
    • Gérer les erreurs avec élégance

Améliorations futures

Considérez ces améliorations pour votre mise en œuvre :

  • Fonctionnalité Annuler/Rétablir
  • Règles de validation des formulaires
  • Implémentation des limites d'erreur

Résultats

Building a Data Table with Optimistic Updates

Demande une fois complétée

Building a Data Table with Optimistic Updates

Conclusion

Cette implémentation montre comment créer un système de gestion de données robuste à l'aide de modèles React modernes. La combinaison de TanStack Query, de Mantine UI et de mises à jour optimistes et réfléchies crée une expérience utilisateur fluide et professionnelle.

N'oubliez pas de :

  • Gardez vos composants concentrés et maintenables
  • Gérer tous les états possibles (chargement, erreur, succès)
  • Utilisez TypeScript pour une meilleure qualité de code
  • Prenez en compte l'expérience utilisateur dans votre implémentation

Quels défis avez-vous rencontrés pour mettre en œuvre des mises à jour optimistes dans vos applications React ? Partagez vos expériences dans les commentaires ci-dessous.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal