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
1087 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!

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