colisdocs

React

@colis/react : envoyer un fichier, retrouver un colis par son code, et un champ de saisie qui corrige le code pendant la frappe.

@colis/react sert à construire votre propre interface d’envoi ou de retrait : un espace client dans votre app, un formulaire de dépôt sur votre site. La page de livraison s’en sert pour son champ « Vous avez déjà un code ? ».

Le paquet ne voit jamais un identifiant de stockage et n’embarque aucun client S3 : il dépend de @colis/protocol et de nanoid, avec React en dépendance pair. Chaque export est un hook client, et le build porte 'use client', donc il s’utilise tel quel dans l’App Router.

Le fournisseur

Pointez-le vers les routes du protocole, par exemple celles de votre page de livraison :

app/providers.tsx
'use client'

import { ColisProvider } from '@colis/react'

export function Providers({ children }: { children: React.ReactNode }) {
  return <ColisProvider baseUrl="/api/transfers">{children}</ColisProvider>
}

headers ajoute un en-tête à chaque requête, par exemple le DROP_PASSWORD en jeton Bearer. client remplace le client entier : c’est ainsi qu’on pilote les hooks en test, sans réseau.

Envoyer un fichier

import { useSendTransfer } from '@colis/react'

function EnvoyerUnLivrable() {
  const { sendFile, transfer, isPending, error } = useSendTransfer()

  return (
    <>
      <input
        type="file"
        disabled={isPending}
        onChange={(event) => event.target.files?.[0] && sendFile(event.target.files[0])}
      />
      {transfer && <p>Code à transmettre au client : {transfer.code}</p>}
      {error && <p role="alert">{error.message}</p>}
    </>
  )
}

sendFile(file, filename?) garde le nom et le type du File. send(data, { version, device }) envoie un objet JSON plutôt qu’un fichier.

Les échecs arrivent dans error plutôt que de rejeter : un gestionnaire d’événement n’a pas besoin d’un try/catch autour de chaque appel. L’appel renvoie null quand il a échoué.

Les hooks parlent le protocole, rien de plus. Les options de la page de livraison (durée, mot de passe, message, usage unique) voyagent dans des en-têtes x-drop-* que les hooks n’envoient pas : un colis créé ainsi prend les valeurs par défaut du déploiement. De même, l’accusé de réception et la réponse du client passent par les routes /status et /verdict, à appeler avec fetch : voir Suivi et validation.

Retrouver un colis

import { useReceiveTransfer, useSyncCodeInput } from '@colis/react'

function RetrouverUnColis() {
  const input = useSyncCodeInput()
  const { load, loadBytes, transfer, notFound, isPending } = useReceiveTransfer()

  return (
    <>
      <input {...input.inputProps} placeholder="K7QP2M4X" />
      <button onClick={() => input.code && load(input.code)} disabled={!input.isComplete || isPending}>
        Chercher
      </button>

      {notFound && <p>Code inconnu ou expiré.</p>}
      {transfer && (
        <p>
          {transfer.filename} · envoyé le {new Date(transfer.createdAt).toLocaleString('fr-FR')}
        </p>
      )}
    </>
  )
}

load(code) lit les métadonnées ; loadBytes(code) télécharge le contenu ; burn(code) détruit le colis. notFound distingue un code inconnu ou expiré d’une vraie erreur.

Le champ de code

useSyncCodeInput() corrige dans le navigateur, avant toute requête : séparateurs retirés, casse unifiée, O, I et L lus comme 0, 1 et 1 quand l’alphabet le permet.

ChampContenu
valueCe que la personne a tapé, intact
codeLa forme canonique à envoyer, null tant qu’elle n’en est pas une
isCompleteVrai quand code a la longueur configurée
errorDéfini quand un caractère ne peut pas appartenir à l’alphabet
inputPropsLes attributs du champ : clavier, saisie automatique, correction

Le champ n’est jamais réécrit sous le curseur : ce qui est tapé reste dans value, la forme corrigée à côté. Passez la même forme que le serveur (useSyncCodeInput({ length, alphabet })) et le clavier suit : un alphabet de chiffres donne inputMode="numeric".

Concurrence

Chaque appel annule le précédent, une réponse tardive d’un appel remplacé est ignorée, et rien n’est écrit après le démontage. Quelqu’un qui martèle le bouton ne se retrouve pas devant la requête qui a fini la dernière par hasard.

Référence

HookRenvoie
useSendTransfer()send, sendFile, transfer, status, isPending, error, reset
useReceiveTransfer()load, loadBytes, burn, transfer, data, notFound, status, isPending, error, reset
useSyncCodeInput()value, setValue, code, isComplete, error, reset, inputProps
useTransferClient()Le client sous-jacent, pour ce que les hooks ne couvrent pas

status vaut 'idle' | 'pending' | 'success' | 'error'. isTransferError et TransferError sont réexportés, pour réagir à un code d’erreur sans seconde dépendance.

Sur cette page