Maison > Article > interface Web > De Next.js à React Edge avec Cloudflare Workers : une histoire de libération
Tout a commencé par une facture de Vercel. Non, cela a en fait commencé bien plus tôt – avec de petites frustrations qui se sont accumulées. La nécessité de payer pour des fonctionnalités de base telles que la protection DDoS, des journaux plus détaillés ou même un pare-feu décent, des files d'attente de création, etc. Le sentiment d’être piégé dans un enfermement de plus en plus coûteux avec un fournisseur.
"Et le pire de tout : nos précieux en-têtes SEO ont tout simplement cessé d'être rendus sur le serveur dans une application utilisant le routeur de pages. Un vrai casse-tête pour tout développeur ! ?"
Mais ce qui m'a vraiment fait repenser tout, c'est la direction que prenait Next.js. L'introduction du client d'utilisation et du serveur d'utilisation - des directives qui, en théorie, devraient simplifier le développement, mais qui, en pratique, ajoutent une autre couche de complexité à gérer. C'était comme si nous revenions à l'époque de PHP, marquant les fichiers avec des directives pour leur indiquer où ils doivent s'exécuter.
Et ça ne s'arrête pas là. L'App Router, une idée intéressante, mais mise en œuvre d'une manière qui a créé un cadre pratiquement nouveau au sein de Next.js. Du coup, nous avions deux manières complètement différentes de faire la même chose. L'« ancien » et le « nouveau » – avec des comportements subtilement différents et des pièges cachés.
C'est à ce moment-là que j'ai réalisé : pourquoi ne pas profiter de l'incroyable infrastructure de Cloudflare avec Workers fonctionnant à la périphérie, R2 pour le stockage, KV pour les données distribuées... Plus, bien sûr, l'incroyable protection DDoS, le CDN global, le pare-feu, les règles. pour les pages, les itinéraires et tout ce que Cloudflare propose.
Et le meilleur : un modèle au juste prix, où vous payez ce que vous utilisez, sans surprise.
C'est ainsi qu'est né React Edge. Un framework qui n'essaie pas de réinventer la roue, mais offre plutôt une expérience de développement vraiment simple et moderne.
Quand j'ai commencé à développer React Edge, j'avais un objectif clair : créer un framework qui avait du sens. Plus besoin de se battre avec des directives confuses, plus besoin de payer des fortunes pour des fonctionnalités de base et, surtout, plus besoin de gérer la complexité artificielle créée par la séparation client/serveur. Je voulais de la vitesse, quelque chose qui offre des performances sans sacrifier la simplicité. Tirant parti de ma connaissance de l'API React et de mes années en tant que développeur Javascript et Golang, je savais exactement comment gérer les flux et le multiplexage pour optimiser le rendu et la gestion des données.
Cloudflare Workers, avec sa puissante infrastructure et sa présence mondiale, m'a offert l'environnement idéal pour explorer ces possibilités. Je voulais quelque chose de véritablement hybride, et cette combinaison d'outils et d'expérience est ce qui a donné vie à React Edge : un framework qui résout de vrais problèmes avec des solutions modernes et efficaces.
React Edge apporte une approche révolutionnaire au développement de React. Imaginez pouvoir écrire une classe sur le serveur et l'appeler directement depuis le client, avec une saisie complète et aucune configuration. Imaginez un système de mise en cache distribué qui « fonctionne tout simplement », permettant l'invalidation par des balises ou des préfixes. Imaginez pouvoir partager l'état entre le serveur et le client de manière transparente et sécurisée. En plus de simplifier l'authentification et d'apporter une approche d'internationalisation efficace, CLI et bien plus encore.
Votre communication RPC est si naturelle qu'elle semble magique : vous écrivez des méthodes dans une classe et vous les appelez depuis le client comme si elles étaient locales. Le système de multiplexage intelligent garantit que même si plusieurs composants effectuent le même appel, une seule requête est adressée au serveur. Le cache éphémère évite les requêtes répétées inutiles, et tout cela fonctionne aussi bien sur le serveur que sur le client.
L'un des points les plus puissants est le hook app.useFetch, qui unifie l'expérience de récupération de données. Sur le serveur, il précharge les données pendant SSR ; sur le client, il s'hydrate automatiquement avec ces données et permet des mises à jour à la demande. Et avec la prise en charge de l'interrogation automatique et de la réactivité basée sur les dépendances, la création d'interfaces dynamiques n'a jamais été aussi simple.
Mais ça ne s'arrête pas là. Le framework offre un système de routage puissant (inspiré du fantastique Hono), une gestion des actifs intégrée à Cloudflare R2 et une manière élégante de gérer les erreurs via la classe HttpError. Le middleware peut facilement envoyer des données au client via un magasin partagé, et tout est automatiquement masqué pour des raisons de sécurité.
Le plus impressionnant ? Presque tout le code du framework est hybride. Il n'existe pas de version « client » et de « serveur » : le même code fonctionne dans les deux environnements, s'adaptant automatiquement au contexte. Le client ne reçoit que ce dont il a besoin, ce qui rend le package final extrêmement optimisé.
Et cerise sur le gâteau : tout cela fonctionne sur l'infrastructure Edge Cloudflare Workers, offrant des performances exceptionnelles à un coût équitable. Pas de surprise sur la facture, pas de fonctionnalités de base cachées derrière des forfaits entreprise forcés, juste un framework solide qui permet de se concentrer sur ce qui compte vraiment : créer des applications incroyables. De plus, React Edge exploite l'ensemble de l'écosystème Cloudflare, y compris les files d'attente, les objets durables, le stockage KV, etc., pour fournir une base robuste et évolutive pour vos applications.
Vite a été utilisé comme base, à la fois pour l'environnement de développement et pour les tests et la construction. Vite, avec sa vitesse impressionnante et son architecture moderne, permet un flux de travail agile et efficace. Cela accélère non seulement le développement, mais optimise également le processus de construction, garantissant que le code est compilé rapidement et avec précision. Sans aucun doute, Vite était le choix parfait pour React Edge.
Vous êtes-vous déjà demandé à quoi cela ressemblerait de développer des applications React sans vous soucier de la barrière client/serveur ? Sans avoir à mémoriser des dizaines de directives comme use client ou use server ? Et encore mieux : et si vous pouviez appeler les fonctions du serveur comme si elles étaient locales, avec une saisie complète et aucune configuration ?
Et le meilleur : tout cela fonctionne à la fois sur le serveur et sur le client, sans avoir à marquer quoi que ce soit avec use client ou use server. Le framework sait quoi faire en fonction du contexte. On y va ?
Imaginez pouvoir faire ceci :
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
Oubliez tout ce que vous savez sur la récupération de données dans React. L'app.useFetch de React Edge apporte une approche complètement nouvelle et puissante. Imaginez un crochet qui :
Voyons cela en action :
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
L'exemple ci-dessus cache une fonctionnalité puissante : le multiplexage intelligent. Lorsque vous utilisez ctx.rpc.batch, React Edge ne se contente pas de regrouper les appels : il déduplique automatiquement les appels identiques :
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
L'une des parties les plus impressionnantes est la façon dont useFetch gère le SSR :
// Primeiro, definimos nossa API no servidor class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Cache automático por 5 minutos return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Agora, no cliente, a mágica acontece const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Busca reativa com debounce inteligente const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Quando filters muda, refaz a busca deps: [filters], // Mas espera 300ms de 'silêncio' antes de buscar depsDebounce: { filters: 300 } } ); // Agora, vamos buscar os detalhes das propriedades encontradas const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // Isso parece fazer múltiplas chamadas, mas... return ctx.rpc.batch([ // Na verdade, tudo é multiplexado em uma única requisição! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Atualiza sempre que searchResults mudar deps: [searchResults] } ); // Interface bonita e responsiva return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Erro na busca: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
Le système RPC de React Edge a été conçu dans un souci de sécurité et d'encapsulation. Tout dans une classe RPC n'est pas automatiquement exposé au client :
const PropertyListingPage = () => { const { data } = app.useFetch(async (ctx) => { // Mesmo que você faça 100 chamadas idênticas... return ctx.rpc.batch([ ctx.rpc.getProperty('123'), ctx.rpc.getProperty('123'), // mesma chamada ctx.rpc.getProperty('456'), ctx.rpc.getProperty('456'), // mesma chamada ]); }); // Mas na realidade: // 1. O batch agrupa todas as chamadas em UMA única requisição HTTP // 2. Chamadas idênticas são deduplicas automaticamente // 3. O resultado é distribuído corretamente para cada posição do array // 4. A tipagem é mantida para cada resultado individual! // Entao.. // 1. getProperty('123') // 2. getProperty('456') // E os resultados são distribuídos para todos os chamadores! };
L'une des fonctionnalités les plus puissantes de RPC est la possibilité d'organiser les API en hiérarchies :
const ProductPage = ({ productId }: Props) => { const { data, loaded, loading, error } = app.useFetch( async (ctx) => ctx.rpc.getProduct(productId), { // Controle fino de quando executar shouldFetch: ({ worker, loaded }) => { // No worker (SSR): sempre busca if (worker) return true; // No cliente: só busca se não tiver dados return !loaded; } } ); // No servidor: // 1. useFetch faz a chamada RPC // 2. Dados são serializados e enviados ao cliente // 3. Componente renderiza com os dados // No cliente: // 1. Componente hidrata com os dados do servidor // 2. Não faz nova chamada (shouldFetch retorna false) // 3. Se necessário, pode refazer a chamada com data.fetch() return ( <Suspense fallback={<ProductSkeleton />}> <ProductView product={data} loading={loading} error={error} /> </Suspense> ); };
L'organisation des API en hiérarchies apporte plusieurs avantages :
Le système RPC de React Edge rend la communication client-serveur si naturelle que vous oublierez presque que vous passez des appels à distance. Et grâce à la possibilité d'organiser les API en hiérarchies, vous pouvez créer des structures complexes tout en gardant votre code organisé et sécurisé.
React Edge apporte un système d'internationalisation élégant et flexible qui prend en charge l'interpolation variable et le formatage complexe sans bibliothèques lourdes.
class PaymentsAPI extends Rpc { // Propriedades nunca são expostas private stripe = new Stripe(process.env.STRIPE_KEY); // Métodos começando com $ são privados private async $validateCard(card: CardInfo) { return await this.stripe.cards.validate(card); } // Métodos começando com _ também são privados private async _processPayment(amount: number) { return await this.stripe.charges.create({ amount }); } // Este método é público e acessível via RPC async createPayment(orderData: OrderData) { // Validação interna usando método privado const validCard = await this.$validateCard(orderData.card); if (!validCard) { throw new HttpError(400, 'Invalid card'); } // Processamento usando outro método privado const payment = await this._processPayment(orderData.amount); return payment; } } // No cliente: const PaymentForm = () => { const { rpc } = app.useContext<App.Context>(); // ✅ Isso funciona const handleSubmit = () => rpc.createPayment(data); // ❌ Isso não é possível - métodos privados não são expostos const invalid1 = () => rpc.$validateCard(data); const invalid2 = () => rpc._processPayment(100); // ❌ Isso também não funciona - propriedades não são expostas const invalid3 = () => rpc.stripe; };
Utilisation dans le code :
// APIs aninhadas para melhor organização class UsersAPI extends Rpc { // Subclasse para gerenciar preferences preferences = new UserPreferencesAPI(); // Subclasse para gerenciar notificações notifications = new UserNotificationsAPI(); async getProfile(id: string) { return this.db.users.findById(id); } } class UserPreferencesAPI extends Rpc { async getTheme(userId: string) { return this.db.preferences.getTheme(userId); } async setTheme(userId: string, theme: Theme) { return this.db.preferences.setTheme(userId, theme); } } class UserNotificationsAPI extends Rpc { // Métodos privados continuam privados private async $sendPush(userId: string, message: string) { await this.pushService.send(userId, message); } async getSettings(userId: string) { return this.db.notifications.getSettings(userId); } async notify(userId: string, notification: Notification) { const settings = await this.getSettings(userId); if (settings.pushEnabled) { await this.$sendPush(userId, notification.message); } } } // No cliente: const UserProfile = () => { const { rpc } = app.useContext<App.Context>(); const { data: profile } = app.useFetch( async (ctx) => { // Chamadas aninhadas são totalmente tipadas const [user, theme, notificationSettings] = await ctx.rpc.batch([ // Método da classe principal ctx.rpc.getProfile('123'), // Método da subclasse de preferências ctx.rpc.preferences.getTheme('123'), // Método da subclasse de notificações ctx.rpc.notifications.getSettings('123') ]); return { user, theme, notificationSettings }; } ); // ❌ Métodos privados continuam inacessíveis const invalid = () => rpc.notifications.$sendPush('123', 'hello'); };
React Edge détecte et charge automatiquement vos traductions et peut facilement enregistrer les préférences de l'utilisateur dans les cookies. Mais vous vous y attendiez déjà, n'est-ce pas ?
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
L'authentification a toujours été un problème dans les applications Web. Gestion des jetons JWT, des cookies sécurisés, de la revalidation : tout cela nécessite généralement beaucoup de code passe-partout. React Edge change complètement cela.
Voyez à quel point il est simple de mettre en œuvre un système d'authentification complet :
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
// Primeiro, definimos nossa API no servidor class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Cache automático por 5 minutos return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Agora, no cliente, a mágica acontece const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Busca reativa com debounce inteligente const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Quando filters muda, refaz a busca deps: [filters], // Mas espera 300ms de 'silêncio' antes de buscar depsDebounce: { filters: 300 } } ); // Agora, vamos buscar os detalhes das propriedades encontradas const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // Isso parece fazer múltiplas chamadas, mas... return ctx.rpc.batch([ // Na verdade, tudo é multiplexado em uma única requisição! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Atualiza sempre que searchResults mudar deps: [searchResults] } ); // Interface bonita e responsiva return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Erro na busca: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
Zéro passe-partout
Sécurité par défaut
Saisie complète
Intégration transparente
const PropertyListingPage = () => { const { data } = app.useFetch(async (ctx) => { // Mesmo que você faça 100 chamadas idênticas... return ctx.rpc.batch([ ctx.rpc.getProperty('123'), ctx.rpc.getProperty('123'), // mesma chamada ctx.rpc.getProperty('456'), ctx.rpc.getProperty('456'), // mesma chamada ]); }); // Mas na realidade: // 1. O batch agrupa todas as chamadas em UMA única requisição HTTP // 2. Chamadas idênticas são deduplicas automaticamente // 3. O resultado é distribuído corretamente para cada posição do array // 4. A tipagem é mantida para cada resultado individual! // Entao.. // 1. getProperty('123') // 2. getProperty('456') // E os resultados são distribuídos para todos os chamadores! };
L'une des fonctionnalités les plus puissantes de React Edge est sa capacité à partager l'état en toute sécurité entre le travailleur et le client. Voyons comment cela fonctionne :
const ProductPage = ({ productId }: Props) => { const { data, loaded, loading, error } = app.useFetch( async (ctx) => ctx.rpc.getProduct(productId), { // Controle fino de quando executar shouldFetch: ({ worker, loaded }) => { // No worker (SSR): sempre busca if (worker) return true; // No cliente: só busca se não tiver dados return !loaded; } } ); // No servidor: // 1. useFetch faz a chamada RPC // 2. Dados são serializados e enviados ao cliente // 3. Componente renderiza com os dados // No cliente: // 1. Componente hidrata com os dados do servidor // 2. Não faz nova chamada (shouldFetch retorna false) // 3. Se necessário, pode refazer a chamada com data.fetch() return ( <Suspense fallback={<ProductSkeleton />}> <ProductView product={data} loading={loading} error={error} /> </Suspense> ); };
Le système de routage de React Edge est inspiré de Hono, mais avec des super pouvoirs pour SSR :
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
React Edge dispose d'un puissant système de mise en cache qui fonctionne à la fois pour les données JSON et les pages entières :
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
Le composant Link est une solution intelligente et performante de préchargement de ressources côté client, assurant une navigation plus fluide et plus rapide aux utilisateurs. Sa fonctionnalité de prélecture est activée lors du survol du curseur sur le lien, profitant du moment d'inactivité de l'utilisateur pour demander à l'avance les données de destination.
Comment ça marche ?
Prélecture conditionnelle : L'attribut de prélecture (actif par défaut) contrôle si le préchargement sera effectué.
Smart Cache : Un Set est utilisé pour stocker les liens déjà préchargés, évitant ainsi les appels redondants.
Mouse Enter : Lorsque l'utilisateur passe le curseur sur le lien, la fonction handleMouseEnter vérifie si un préchargement est nécessaire et, si c'est le cas, lance une demande de récupération vers la destination.
Error Safe : tout échec dans la requête est supprimé, garantissant que le comportement du composant n'est pas affecté par des erreurs réseau momentanées.
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Lorsque l'utilisateur passe la souris sur le lien « À propos de nous », le composant commence à précharger les données de la page /about, offrant une transition presque instantanée. Idée géniale, non ? Mais je l'ai vu dans la documentation de React.dev.
app.useContext est le hook fondamental de React Edge, donnant accès à l'ensemble du contexte du travailleur :
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
Le hook app.useContext comble le fossé entre le travailleur et le client. Il vous permet de créer des fonctionnalités qui s'appuient sur un état partagé, une récupération de données sécurisée et un rendu contextuel sans code répétitif. Cela simplifie les applications complexes, les rendant plus faciles à maintenir et plus rapides à développer.
Le hook app.useUrlState maintient l'état de votre application synchronisé avec les paramètres d'URL, vous donnant un contrôle précis sur ce qui est inclus dans l'URL, comment l'état est sérialisé et quand il est mis à jour.
// Primeiro, definimos nossa API no servidor class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Cache automático por 5 minutos return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Agora, no cliente, a mágica acontece const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Busca reativa com debounce inteligente const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Quando filters muda, refaz a busca deps: [filters], // Mas espera 300ms de 'silêncio' antes de buscar depsDebounce: { filters: 300 } } ); // Agora, vamos buscar os detalhes das propriedades encontradas const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // Isso parece fazer múltiplas chamadas, mas... return ctx.rpc.batch([ // Na verdade, tudo é multiplexado em uma única requisição! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Atualiza sempre que searchResults mudar deps: [searchResults] } ); // Interface bonita e responsiva return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Erro na busca: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
État initial
Options :
Le hook app.useStorageState vous permet de conserver l'état dans le navigateur en utilisant localStorage ou sessionStorage avec une prise en charge complète de la saisie.
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Anti-rebond des valeurs réactives en toute simplicité :
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
Maintenir des tableaux de valeurs uniques en tapant :
app.useDistinct est un hook spécialisé dans la détection du moment où une valeur a réellement changé, avec prise en charge de la comparaison approfondie et de l'anti-rebond :
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Les hooks React Edge ont été conçus pour fonctionner en harmonie, offrant une expérience de développement fluide et typée. Les combiner permet de créer des interfaces complexes et réactives avec beaucoup moins de code.
La CLI React Edge a été conçue pour simplifier la vie des développeurs en regroupant les outils essentiels dans une interface unique et intuitive. Que vous soyez débutant ou expert, la CLI garantit que vous pouvez configurer, développer, tester et déployer des projets efficacement et sans tracas.
Fonctionnalités clés
Je suis fier de vous annoncer que la première application de production utilisant React Edge fonctionne désormais ! Il s'agit d'une société immobilière brésilienne, Lopes Imóveis, qui profite déjà de toutes les performances et de la flexibilité du framework.
Sur le site de l'agence immobilière, les propriétés sont chargées en cache pour optimiser la recherche et offrir une expérience plus fluide aux utilisateurs. Comme il s’agit d’un site Web extrêmement dynamique, le cache de route utilise un TTL de seulement 10 secondes, combiné à la stratégie périmée pendant la revalidation. Cela garantit que le site fournit des données à jour avec des performances exceptionnelles, même lors des revalidations en arrière-plan.
De plus, les recommandations pour des propriétés similaires sont calculées efficacement et occasionnellement en arrière-plan, et enregistrées directement dans le cache de Cloudflare, à l'aide du système de cache intégré à RPC. Cette approche réduit le temps de réponse aux requêtes ultérieures et rend les recommandations d'interrogation presque instantanées. De plus, toutes les images sont stockées sur Cloudflare R2, offrant un stockage évolutif et distribué sans recourir à des fournisseurs externes.
Et bientôt nous aurons également le lancement d'un gigantesque projet de marketing automatisé pour Easy Auth, démontrant encore davantage le potentiel de cette technologie.
Et voilà, chers lecteurs, nous arrivons au terme de cette aventure à travers l'univers React Edge ! Je sais qu'il y a encore une mer de choses incroyables à explorer, comme les authentifications les plus simples comme Basic et Bearer, et d'autres petits secrets qui rendent la vie quotidienne d'un développeur beaucoup plus heureuse. Mais calme-toi ! L’idée est de proposer à l’avenir des articles plus détaillés pour approfondir chacune de ces fonctionnalités.
Et, spoiler : bientôt React Edge sera open source et correctement documenté ! Équilibrer le développement, le travail, l'écriture et un peu de vie sociale n'est pas facile, mais l'excitation de voir cette merveille en action, surtout avec la vitesse absurde fournie par l'infrastructure de Cloudflare, est le carburant qui m'anime. Alors retenez votre anxiété, car le meilleur est à venir ! ?
En attendant, si vous souhaitez commencer à explorer et tester dès maintenant, le package est désormais disponible sur NPM : React Edge sur NPM..
Mon email est feliperohdee@gmail.com, et je suis toujours ouvert aux commentaires, ce n'est que le début de ce voyage, des suggestions et des critiques constructives. Si vous avez aimé ce que vous avez lu, partagez-le avec vos amis et collègues et gardez un œil sur les nouveautés à venir. Merci de m'avoir suivi jusqu'ici, et à la prochaine ! ???
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!