Tout a commencé par une facture Vercel. Non, en fait, cela a 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 comme la protection DDoS, des journaux plus détaillés, ou même un pare-feu décent, la création de files d'attente, etc. Le sentiment d'être piégé dans un verrouillage fournisseur de plus en plus coûteux.
"Et le pire de tout : nos précieux en-têtes SEO ont tout simplement arrêté de s'afficher 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 des directives use client, use server qui, en théorie, devraient simplifier le développement mais, en pratique, a ajouté une autre couche de complexité à gérer. C'était comme revenir à l'époque PHP, marquer les fichiers avec des directives pour dicter où ils devaient 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 presque entièrement nouveau au sein de Next.js. Soudain, il y avait deux manières complètement différentes de faire la même chose – l’« ancienne » et la « nouvelle » – avec des comportements subtilement différents et des pièges cachés.
C'est à ce moment-là que j'ai compris : pourquoi ne pas tirer parti de l'incroyable infrastructure de Cloudflare avec Workers fonctionnant en périphérie, R2 pour le stockage, KV pour les données distribuées... Avec, bien sûr, l'incroyable protection DDoS, le CDN global, le pare-feu. , les règles de page et le routage, et tout ce que Cloudflare a à offrir.
Et le meilleur : un modèle de tarification équitable où vous payez ce que vous utilisez, sans surprise.
C'est ainsi qu'est né React Edge. Un framework qui ne vise pas à réinventer la roue mais plutôt à offrir 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 lutter avec des directives déroutantes, plus besoin de payer des frais exorbitants pour les fonctionnalités de base et, plus important encore, plus besoin de gérer la complexité artificielle causée par la séparation client/serveur. Je voulais de la rapidité : un framework qui offre des performances sans sacrifier la simplicité. Tirant parti de ma connaissance de l'API de React et de mes années d'expérience 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, a fourni 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 a donné vie à React Edge : un framework qui résout des problèmes du monde réel avec des solutions modernes et efficaces.
React Edge introduit une approche révolutionnaire du développement de React. Imaginez écrire une classe sur le serveur et l'appeler directement depuis le client, avec une sécurité de type totale 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 partager l’état de manière transparente et sécurisée entre le serveur et le client. Ajoutez une authentification simplifiée, un système d'internationalisation efficace, une CLI, et bien plus encore.
Sa communication RPC semble presque magique : vous écrivez des méthodes dans une classe et 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 envoyée au serveur. La mise en cache éphémère évite les requêtes répétées inutiles et tout fonctionne de manière transparente à la fois sur le serveur et sur le client.
L'une des fonctionnalités les plus puissantes 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 prend en charge les mises à jour à la demande. 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 ce n’est pas tout. Le framework offre un système de routage puissant (inspiré du fantastique Hono), une gestion d'actifs intégrée avec Cloudflare R2 et une manière élégante de gérer les erreurs via la classe HttpError. Les middlewares peuvent facilement envoyer des données au client via un magasin partagé, et tout est automatiquement masqué pour des raisons de sécurité.
La partie la plus impressionnante ? Presque tout le code du framework est hybride. Il n’existe pas de version « client » et de version « serveur » : le même code fonctionne dans les deux environnements, s’adaptant automatiquement au contexte. Le client reçoit uniquement 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 périphérique Cloudflare Workers, offrant des performances exceptionnelles à un coût équitable. Pas de factures surprises, pas de fonctionnalités de base enfermées dans des forfaits d'entreprise coûteux : juste un cadre solide qui vous permet de vous concentrer sur ce qui compte vraiment : créer des applications étonnantes. De plus, React Edge exploite l'écosystème de Cloudflare, notamment les files d'attente, les objets durables, le stockage KV, etc., fournissant une base robuste et évolutive pour vos applications.
Vite a été utilisé comme base pour l'environnement de développement, les tests et le processus de construction. Avec sa vitesse impressionnante et son architecture moderne, Vite permet un flux de travail agile et efficace. Il accélère non seulement le développement, mais optimise également le processus de construction, garantissant une compilation rapide et précise. 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 mémoriser des dizaines de directives comme use client ou use server ? Mieux encore : 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 de manière transparente à la fois sur le serveur et sur le client sans rien marquer comme utiliser le client ou utiliser le serveur. Le framework sait automatiquement quoi faire en fonction du contexte. On y plonge ?
Imaginez pouvoir faire ceci :
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! 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) => { // Configure CORS // Validate request // Handle errors // Serialize response // ...100 lines later... } // 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]); // ... rest of the component }
Oubliez tout ce que vous savez sur la récupération de données dans React. Le hook app.useFetch de React Edge introduit une approche complètement nouvelle et puissante. Imaginez un crochet qui :
Voyons-le en action :
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! 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 non seulement regroupe les appels, mais il déduplique également automatiquement les appels identiques :
// pages/api/search.ts export default async handler = (req, res) => { // Configure CORS // Validate request // Handle errors // Serialize response // ...100 lines later... } // 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]); // ... rest of the component }
// First, define your API on the server class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Automatic caching for 5 minutes return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Now, on the client, the magic happens const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Reactive search with intelligent debounce const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Re-fetch when filters change deps: [filters], // Wait 300ms of "silence" before fetching depsDebounce: { filters: 300 } } ); // Fetch property details for the found results const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // This looks like multiple calls, but... return ctx.rpc.batch([ // Everything is multiplexed into a single request! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Refresh when searchResults change deps: [searchResults] } ); // A beautiful and responsive interface return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Search error: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
Le système RPC de React Edge est 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) => { // Even if you make 100 identical calls... return ctx.rpc.batch([ ctx.rpc.getProperty('123'), ctx.rpc.getProperty('123'), // same call ctx.rpc.getProperty('456'), ctx.rpc.getProperty('456'), // same call ]); }); // Behind the scenes: // 1. The batch groups all calls into ONE single HTTP request. // 2. Identical calls are deduplicated automatically. // 3. Results are distributed correctly to each position in the array. // 4. Typing is maintained for each individual result! // Actual RPC calls: // 1. getProperty('123') // 2. getProperty('456') // Results are distributed correctly to all callers! };
L'une des fonctionnalités les plus puissantes de RPC est la possibilité d'organiser les API en hiérarchies :
One of the most impressive parts is how useFetch handles SSR: const ProductPage = ({ productId }: Props) => { const { data, loaded, loading, error } = app.useFetch( async (ctx) => ctx.rpc.getProduct(productId), { // Fine-grained control over when to fetch shouldFetch: ({ worker, loaded }) => { // On the worker (SSR): always fetch if (worker) return true; // On the client: fetch only if no data is loaded return !loaded; } } ); // On the server: // 1. `useFetch` makes the RPC call. // 2. Data is serialized and sent to the client. // 3. Component renders with the data. // On the client: // 1. Component hydrates with server data. // 2. No new call is made (shouldFetch returns false). // 3. If necessary, you can re-fetch with `data.fetch()`. return ( <Suspense fallback={<ProductSkeleton />}> <ProductView product={data} loading={loading} error={error} /> </Suspense> ); };
L'organisation des API en hiérarchies offre 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. Avec la possibilité d'organiser les API en hiérarchies, vous pouvez créer des structures complexes tout en gardant votre code propre et sécurisé.
React Edge introduit un système d'internationalisation élégant et flexible qui prend en charge l'interpolation variable et le formatage complexe sans recourir à des bibliothèques lourdes.
class PaymentsAPI extends Rpc { // Properties are never exposed private stripe = new Stripe(process.env.STRIPE_KEY); // Methods starting with $ are private private async $validateCard(card: CardInfo) { return await this.stripe.cards.validate(card); } // Methods starting with _ are also private private async _processPayment(amount: number) { return await this.stripe.charges.create({ amount }); } // This method is public and accessible via RPC async createPayment(orderData: OrderData) { // Internal validation using a private method const validCard = await this.$validateCard(orderData.card); if (!validCard) { throw new HttpError(400, 'Invalid card'); } // Processing using another private method const payment = await this._processPayment(orderData.amount); return payment; } } // On the client: const PaymentForm = () => { const { rpc } = app.useContext<App.Context>(); // ✅ This works const handleSubmit = () => rpc.createPayment(data); // ❌ These do not work - private methods are not exposed const invalid1 = () => rpc.$validateCard(data); const invalid2 = () => rpc._processPayment(100); // ❌ This also does not work - properties are not exposed const invalid3 = () => rpc.stripe; };
Utilisation dans le code :
// Nested APIs for better organization class UsersAPI extends Rpc { // Subclass to manage preferences preferences = new UserPreferencesAPI(); // Subclass to manage notifications 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 { // Private methods remain private 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); } } } // On the client: const UserProfile = () => { const { rpc } = app.useContext<App.Context>(); const { data: profile } = app.useFetch( async (ctx) => { // Nested calls are fully typed const [user, theme, notificationSettings] = await ctx.rpc.batch([ // Method from the main class ctx.rpc.getProfile('123'), // Method from the preferences subclass ctx.rpc.preferences.getTheme('123'), // Method from the notifications subclass ctx.rpc.notifications.getSettings('123') ]); return { user, theme, notificationSettings }; } ); // ❌ Private methods remain inaccessible const invalid = () => rpc.notifications.$sendPush('123', 'hello'); };
React Edge détecte et charge automatiquement vos traductions. Il permet même de sauvegarder sans effort les préférences de l’utilisateur dans les cookies. Mais bien sûr, vous vous y attendriez, n’est-ce pas ?
// translations/fr.ts export default { 'Good Morning, {name}!': 'Bonjour, {name}!', };
L'authentification a toujours été un problème dans les applications Web. La gestion des jetons JWT, des cookies sécurisés et de la revalidation nécessite souvent beaucoup de code passe-partout. React Edge change complètement cela.
Voici à quel point il est simple de mettre en œuvre un système d'authentification complet :
const WelcomeMessage = () => { const userName = 'John'; return ( <div> {/* Output: Good Morning, John! */} <h1>{__('Good Morning, {name}!', { name: userName })}</h1> </div> ); };
// worker.ts const handler = { fetch: async (request: Request, env: types.Worker.Env, context: ExecutionContext) => { const url = new URL(request.url); const lang = (() => { const lang = url.searchParams.get('lang') || worker.cookies.get(request.headers, 'lang') || request.headers.get('accept-language') || ''; if (!lang || !i18n[lang]) { return 'en-us'; } return lang; })(); const workerApp = new AppWorkerEntry({ i18n: { en: await import('./translations/en'), pt: await import('./translations/pt'), es: await import('./translations/es') } }); const res = await workerApp.fetch(); if (url.searchParams.has('lang')) { return new Response(res.body, { headers: worker.cookies.set(res.headers, 'lang', lang) }); } return res; } };
Zéro passe-partout
Sécurité par défaut
Saisie complète
Intégration transparente
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
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. Voici comment cela fonctionne :
// pages/api/search.ts export default async handler = (req, res) => { // Configure CORS // Validate request // Handle errors // Serialize response // ...100 lines later... } // 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]); // ... rest of the component }
Le système de routage de React Edge est inspiré de Hono, mais avec des fonctionnalités améliorées pour SSR :
// First, define your API on the server class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Automatic caching for 5 minutes return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Now, on the client, the magic happens const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Reactive search with intelligent debounce const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Re-fetch when filters change deps: [filters], // Wait 300ms of "silence" before fetching depsDebounce: { filters: 300 } } ); // Fetch property details for the found results const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // This looks like multiple calls, but... return ctx.rpc.batch([ // Everything is multiplexed into a single request! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Refresh when searchResults change deps: [searchResults] } ); // A beautiful and responsive interface return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Search error: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
React Edge comprend un puissant système de mise en cache qui fonctionne de manière transparente pour les données JSON et les pages entières. Ce système de mise en cache prend en charge le balisage intelligent et l'invalidation basée sur les préfixes, ce qui le rend adapté à un large éventail de scénarios.
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Le composant Link est une solution intelligente et orientée performances pour précharger les ressources côté client, garantissant une expérience de navigation plus fluide et plus rapide pour les utilisateurs. Sa fonctionnalité de prélecture se déclenche lorsque l'utilisateur survole le lien, profitant des moments d'inactivité pour demander à l'avance les données de destination.
// pages/api/search.ts export default async handler = (req, res) => { // Configure CORS // Validate request // Handle errors // Serialize response // ...100 lines later... } // 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]); // ... rest of the component }
Lorsque l'utilisateur survole le lien « À propos de nous », le composant commence à précharger les données de la page /about, assurant une transition presque instantanée. Idée géniale, n'est-ce pas ? Inspiré de la documentation de React.dev.
Le hook app.useContext est la pierre angulaire de React Edge, offrant un accès transparent à l'ensemble du contexte du travailleur. Il fournit une interface puissante pour gérer le routage, l'état, les appels RPC, etc.
// First, define your API on the server class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Automatic caching for 5 minutes return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Now, on the client, the magic happens const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Reactive search with intelligent debounce const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Re-fetch when filters change deps: [filters], // Wait 300ms of "silence" before fetching depsDebounce: { filters: 300 } } ); // Fetch property details for the found results const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // This looks like multiple calls, but... return ctx.rpc.batch([ // Everything is multiplexed into a single request! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Refresh when searchResults change deps: [searchResults] } ); // A beautiful and responsive interface return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Search error: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
Principales fonctionnalités de app.useContext
Le hook app.useContext comble le fossé entre le travailleur et le client. Il vous permet de créer des fonctionnalités qui reposent sur un état partagé, une récupération de données sécurisée et un rendu contextuel sans passe-partout. 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 de requête d'URL, offrant 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.
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
É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 TypeScript.
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Anti-rebond des valeurs réactives sans effort :
// pages/api/search.ts export default async handler = (req, res) => { // Configure CORS // Validate request // Handle errors // Serialize response // ...100 lines later... } // 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]); // ... rest of the component }
Conservez les tableaux avec des valeurs uniques tout en préservant la sécurité des types.
Le hook app.useDistinct est spécialisé dans la détection du moment où une valeur a réellement changé, avec la prise en charge d'une comparaison approfondie et de l'anti-rebond :
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Les hooks de React Edge sont conçus pour fonctionner en harmonie, offrant une expérience de développement fluide et fortement typée. Leur combinaison permet de créer des interfaces complexes et réactives avec beaucoup moins de code.
La CLI pour React Edge a été conçue pour simplifier la vie des développeurs en rassemblant les outils essentiels dans une interface unique et intuitive. Que vous soyez un développeur débutant ou expérimenté, la CLI garantit que vous pouvez configurer, développer, tester et déployer des projets efficacement et sans effort.
Je suis fier de vous annoncer que la première application de production utilisant React Edge est déjà en ligne ! Il s'agit d'une société immobilière brésilienne, Lopes Imóveis, qui récolte déjà les bénéfices de la performance et de la flexibilité du framework.
Sur leur site Web, les propriétés sont chargées dans le cache pour optimiser la recherche et offrir une expérience utilisateur plus fluide. Puisqu'il s'agit d'un site très dynamique, la mise en cache des routes utilise une durée de vie de seulement 10 secondes, combinée à la stratégie périmée pendant la revalidation. Cela garantit que le site fournit des données mises à 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 de manière efficace et asynchrone en arrière-plan, puis enregistrées directement dans le cache de Cloudflare à l'aide du système de mise en cache RPC intégré. Cette approche réduit les temps de réponse pour les demandes ultérieures et rend les recommandations d'interrogation presque instantanées. Toutes les images sont stockées sur Cloudflare R2, offrant un stockage évolutif et distribué sans recourir à des fournisseurs externes.
Bientôt, nous lancerons également un projet de marketing automatisé massif pour Easy Auth, démontrant encore plus le potentiel de cette technologie.
Et voilà, chers lecteurs, nous avons atteint la fin de ce voyage à travers le monde de React Edge ! Je sais qu’il reste encore une multitude de fonctionnalités incroyables à explorer, comme des options d’authentification plus simples telles que Basic et Bearer, et d’autres astuces qui rendent la journée d’un développeur beaucoup plus heureuse. Mais attendez ! L'idée est de proposer à l'avenir des articles plus détaillés pour approfondir chacune de ces fonctionnalités.
Alerte 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 me permet de continuer. Alors restez connectés, car le meilleur reste à venir ! ?
En attendant, si vous souhaitez commencer à l'explorer et à le tester dès maintenant, le package est déjà disponible sur NPM : React Edge sur NPM..
Mon e-mail est feliperohdee@gmail.com, et je suis toujours ouvert aux commentaires – ce n'est que le début de ce voyage. Les suggestions et critiques constructives sont les bienvenues. Si vous avez apprécié ce que vous avez lu, partagez-le avec vos amis et collègues et restez à l'écoute pour plus de mises à jour. Merci d'avoir lu jusqu'ici et à la prochaine fois ! ???
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!