# De Next.js a React Edge com Cloudflare Workers: Uma História de Libertação # Índice Rápido - [De Next.js a React Edge com Cloudflare Workers: Uma História de Libertação](#de-nextjs-a-react-edge-com-cloudflare-workers-uma-história-de-libertação) - [A Gota D'água](#a-gota-dágua) - [A Alternativa com Cloudflare 😍](#a-alternativa-com-cloudflare-) - [React Edge: O Framework React derivado de todas (ou quase) as dores de um desenvolvedor](#react-edge-o-framework-react-derivado-de-todas-ou-quase-as-dores-de-um-desenvolvedor) - [A Magia do RPC Tipado](#a-magia-do-rpc-tipado) - [O Poder do useFetch: Onde a Mágica Acontece](#o-poder-do-usefetch-onde-a-mágica-acontece) - [Além do useFetch: O arsenal completo](#além-do-usefetch-o-arsenal-completo) - [RPC: A Arte da Comunicação Cliente-Servidor](#rpc-a-arte-da-comunicação-cliente-servidor) - [Um Sistema de i18n que Faz Sentido](#um-sistema-de-i18n-que-faz-sentido) - [Autenticação JWT que "Simplesmente Funciona"](#autenticação-jwt-que-simplesmente-funciona) - [O Store Compartilhado](#o-store-compartilhado) - [Roteamento Elegante](#roteamento-elegante) - [Cache Distribuído com Edge Cache](#cache-distribuído-com-edge-cache) - [Link: O Componente que Pensa à Frente](#link-o-componente-que-pensa-à-frente) - [app.useContext: O Portal para o Edge](#appusecontext-o-portal-para-o-edge) - [app.useUrlState: Estado Sincronizado com a URL](#appuseurlstate-estado-sincronizado-com-a-url) - [app.useStorageState: Estado Persistente](#appusestoragestate-estado-persistente) - [app.useDebounce: Controle de Frequência](#appusedebounce-controle-de-frequência) - [app.useDistinct: Estado sem Duplicatas](#appusedistinct-estado-sem-duplicatas) - [A CLI do React Edge: Potência na Ponta dos Dedos](#a-cli-do-react-edge-potência-na-ponta-dos-dedos) - [Conclusão](#conclusão) ## A Gota D'água Tudo começou com uma fatura da [Vercel](https://vercel.com/). Não, na verdade começou bem antes - com pequenas frustrações que foram se acumulando. A necessidade de pagar por features básicas como proteção DDoS, logs mais detalhados, ou mesmo um firewall decente, filas de builds, etc. A sensação de estar preso em um vendor lock-in cada vez mais caro. "E o pior de tudo: nossos preciosos cabeçalhos de SEO simplesmente deixaram de ser renderizados no servidor em uma aplicação utilizando o `pages router`. Uma verdadeira dor de cabeça para qualquer dev! 😭" Mas o que realmente me fez repensar tudo foi a direção que o [Next.js](https://nextjs.org/) estava tomando. A introdução do `use client`, `use server` - diretivas que, em teoria, deveriam simplificar o desenvolvimento, mas na prática adicionavam mais uma camada de complexidade para gerenciar. Era como se estivéssemos voltando aos tempos do PHP, marcando arquivos com diretivas para dizer onde eles deveriam rodar. E não para por aí. O App Router, uma ideia interessante, mas implementada de forma que criou um framework praticamente novo dentro do [Next.js](https://nextjs.org/). De repente, tínhamos duas formas completamente diferentes de fazer a mesma coisa. A 'velha' e a 'nova' - com comportamentos sutilmente diferentes e armadilhas escondidas. ## A Alternativa com Cloudflare 😍 Foi quando percebi: por que não aproveitar a incrível infraestrutura da [Cloudflare](https://www.cloudflare.com/) com [Workers](https://workers.cloudflare.com/) rodando no edge, [R2](https://www.cloudflare.com/developer-platform/products/r2/) para storage, [KV](https://developers.cloudflare.com/kv/) para dados distribuídos... Além, é claro, a incrível [proteção DDoS](https://www.cloudflare.com/pt-br/lp/ppc/ddos-y), [CDN global](https://www.cloudflare.com/lp/pg-cdn-performance-bundle), [firewall](https://www.cloudflare.com/pt-br/lp/ppc/waf-x), [regras par páginas e rotas](https://developers.cloudflare.com/rules/page-rules/) e tudo mais que a [Cloudflare](https://www.cloudflare.com/) oferece. E o melhor: um modelo de preço justo, onde você paga pelo que usa, sem surpresas. Assim nasceu o [React Edge](https://www.npmjs.com/package/react-edge). Um framework que não tenta reinventar a roda, mas sim proporcionar uma experiência de desenvolvimento verdadeiramente simples e moderna. # React Edge: O Framework React derivado de todas (ou quase) as dores de um desenvolvedor Quando comecei a desenvolver o [React Edge](https://www.npmjs.com/package/react-edge), tinha um objetivo claro: criar um framework que fizesse sentido. Não mais lutar com diretivas confusas, não mais pagar fortunas por recursos básicos, e principalmente, não mais ter que lidar com a complexidade artificial criada pela separação cliente/servidor. Eu queria velocidade, algo que entregasse performance sem sacrificar simplicidade. Aproveitando meu conhecimento da API do React e anos como desenvolvedor Javascript e Golang, sabia exatamente como lidar com streams e multiplexação para otimizar a renderização e o gerenciamento de dados. O [Cloudflare Workers](https://workers.cloudflare.com/), com sua infraestrutura poderosa e presença global, me ofereceu o ambiente perfeito para explorar essas possibilidades. Queria algo que fosse verdadeiramente híbrido, e essa combinação de ferramentas e experiência foi o que deu vida ao [React Edge](https://www.npmjs.com/package/react-edge): um framework que resolve problemas reais com soluções modernas e eficientes. O [React Edge](https://www.npmjs.com/package/react-edge) traz uma abordagem revolucionária para desenvolvimento React. Imagine poder escrever uma classe no servidor e chamá-la diretamente do cliente, com tipagem completa e zero configuração. Imagine um sistema de cache distribuído que "simplesmente funciona", permitindo invalidação por tags ou prefixos. Imagine poder compartilhar estado entre servidor e cliente de forma transparente e segura. Além de simplificar a autenticação e trazer uma abordagem de internacionalização eficiente, CLI e muito mais. Sua comunicação RPC é tão natural que parece mágica - você escreve métodos em uma classe e os chama do cliente como se fossem locais. O sistema de multiplexação inteligente garante que, mesmo que múltiplos componentes façam a mesma chamada, apenas uma requisição seja feita ao servidor. O cache efêmero evita requisições repetidas desnecessárias, e tudo isso funciona tanto no servidor quanto no cliente. Um dos pontos mais poderosos é o hook `app.useFetch`, que unifica a experiência de data fetching. No servidor, ele pré-carrega os dados durante o SSR; no cliente, ele hidrata automaticamente com esses dados e permite atualizações sob demanda. E com suporte a polling automático e reatividade baseada em dependências, criar interfaces dinâmicas nunca foi tão fácil. Mas não para por aí. O framework oferece um sistema de rotas poderoso (inspirado no fantástico [Hono](https://hono.dev/)), gerenciamento de assets integrado com [Cloudflare R2](https://www.cloudflare.com/developer-platform/products/r2/), e uma forma elegante de lidar com erros através da classe HttpError. Os middlewares podem facilmente enviar dados para o cliente através de um store compartilhado, e tudo é ofuscado automaticamente para segurança. O mais impressionante? Quase todo o código do framework é híbrido. Não há uma versão 'cliente' e outra 'servidor' - o mesmo código funciona nos dois ambientes, adaptando-se automaticamente ao contexto. O cliente recebe apenas o que precisa, tornando o bundle final extremamente otimizado. E a cereja do bolo: tudo isso roda na infraestrutura edge do [Cloudflare Workers](https://workers.cloudflare.com/), proporcionando performance excepcional a um custo justo. Sem surpresas na fatura, sem recursos básicos escondidos atrás de planos enterprise forçados, apenas um framework sólido que permite você focar no que realmente importa: criar aplicações incríveis. O [Vite](https://vite.dev/) foi usado como base, tanto para o ambiente de desenvolvimento quanto para testes e build. O Vite, com sua velocidade impressionante e arquitetura moderna, permite um fluxo de trabalho ágil e eficiente. Ele não apenas acelera o desenvolvimento, mas também otimiza o processo de build, garantindo que o código seja compilado de forma rápida e precisa. Sem dúvida, o Vite foi a escolha perfeita para o [React Edge](https://www.npmjs.com/package/react-edge). ## Repensando o Desenvolvimento React para a era do Edge Computing Você já se perguntou como seria desenvolver aplicações React sem se preocupar com a barreira cliente/servidor? Sem precisar decorar dezenas de diretivas como `use client` ou `use server`? E melhor ainda: e se você pudesse chamar funções do servidor como se fossem locais, com tipagem completa e zero configuração? ### Com o React Edge, você não precisa: - Criar rotas de API separadas - Gerenciar estado de loading/error manualmente - Implementar debounce na mão - Se preocupar com serialização/deserialização - Lidar com CORS - Gerenciar tipagem entre cliente/servidor - Lidar com regras de autenticação manualmente - Gerenciar como a internacionalização é feita E o melhor: tudo isso funciona tanto no servidor quanto no cliente, sem precisar marcar nada com use client ou use server. O framework sabe o que fazer baseado no contexto. Vamos lá? ### A Magia do RPC Tipado Imagine poder fazer isso: ```tsx // 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>(); // 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' }) ); }; ``` ### Compare isso com o Next.js/Vercel: ```tsx // 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 } ``` # O Poder do useFetch: Onde a Mágica Acontece ## Data Fetching Repensado Esqueça tudo que você sabe sobre data fetching no React. O `app.useFetch` do [React Edge](https://www.npmjs.com/package/react-edge) traz uma abordagem completamente nova e poderosa. Imagine um hook que: - Pré-carrega dados no servidor durante SSR - Hidrata automaticamente no cliente sem flicker - Mantém tipagem completa entre cliente e servidor - Suporta reatividade com debounce inteligente - Multiplexa chamadas idênticas automaticamente - Permite atualizações programáticas e polling Vamos ver isso em ação: ```tsx // 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({ 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 (
{searchError && ( Erro na busca: {searchError.message} )} refreshDetails()} />
); }; ``` ### A Mágica da Multiplexação O exemplo acima esconde uma característica poderosa: a multiplexação inteligente. Quando você usa ctx.rpc.batch, o [React Edge](https://www.npmjs.com/package/react-edge) não apenas agrupa as chamadas - ele deduplicar chamadas idênticas automaticamente: ```tsx 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! }; ``` ### SSR + Hidratação Perfeita Uma das partes mais impressionantes é como o useFetch lida com SSR: ```tsx 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 ( }> ); }; ``` # Além do useFetch: O arsenal completo ## RPC: A Arte da Comunicação Cliente-Servidor ### Segurança e Encapsulamento O sistema RPC do [React Edge](https://www.npmjs.com/package/react-edge) foi projetado pensando em segurança e encapsulamento. Nem tudo que está em uma classe RPC é automaticamente exposto ao cliente: ```tsx 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>(); // ✅ 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; }; ``` ### Hierarquia de APIs RPC Uma das características mais poderosas do RPC é a capacidade de organizar APIs em hierarquias: ```tsx // 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>(); 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'); }; ``` ### Benefícios da Hierarquia Organizar APIs em hierarquias traz vários benefícios: - Organização Lógica: Agrupe funcionalidades relacionadas de forma intuitiva - Namespace Natural: Evite conflitos de nomes com caminhos claros (users.preferences.getTheme) - Encapsulamento: Mantenha métodos auxiliares privados em cada nível - Manutenibilidade: Cada subclasse pode ser mantida e testada independentemente - Tipagem Completa: O TypeScript entende toda a hierarquia O sistema de RPC do [React Edge](https://www.npmjs.com/package/react-edge) torna a comunicação cliente-servidor tão natural que você quase esquece que está fazendo chamadas remotas. E com a capacidade de organizar APIs em hierarquias, você pode criar estruturas complexas mantendo o código organizado e seguro. ## Um Sistema de i18n que Faz Sentido O [React Edge](https://www.npmjs.com/package/react-edge) traz um sistema de internacionalização elegante e flexível, que suporta interpolação de variáveis e formatação complexa sem bibliotecas pesadas. ```tsx // translations/fr.ts export default { 'Good Morning, {name}!': 'Bonjour, {name}!', }; ``` Uso no código: ```tsx const WelcomeMessage = () => { const userName = 'João'; return (
{/* Output: Bem vindo, João! */}

{__('Good Morning, {name}!', { name: userName })}

); }; ``` #### Configuração Zero O [React Edge](https://www.npmjs.com/package/react-edge) detecta e carrega suas traduções automaticamente, podendo salvar fácilmente nos cookies a preferência do usuário. Mas isso você já esperava, certo? ```tsx // 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 worker = 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; } }; ``` ## Autenticação JWT que "Simplesmente Funciona" A autenticação sempre foi um ponto de dor em aplicações web. Gerenciar tokens JWT, cookies seguros, revalidação - tudo isso geralmente requer muito código boilerplate. O [React Edge](https://www.npmjs.com/package/react-edge) muda isso completamente. Veja como é simples implementar um sistema completo de autenticação: ```tsx class SessionAPI extends Rpc { private auth = new AuthJwt({ // Cookie será automaticamente gerenciado cookie: 'token', // Payload é automaticamente encriptado encrypt: true, // Expiração automática expires: { days: 1 }, secret: process.env.JWT_SECRET }); async signin(credentials: { email: string; password: string }) { // Validação com Zod const validated = loginSchema.parse(credentials); const { headers } = await this.auth.sign(validated)); // Retorna resposta com cookies configurados return this.createResponse( { email: validated.email }, { headers: (await this.auth.sign(validated)) } ); } async getSession(revalidate = false) { // Validação e revalidação automática de token const { headers, payload } = await this.auth.authenticate( this.request.headers, revalidate ); return this.createResponse(payload, { headers }); } async signout() { // Limpa cookies automaticamente const { headers } = await this.auth.destroy(); return this.createResponse(null, { headers }); } } ``` #### Uso no Cliente: Zero Configuração ```tsx const LoginForm = () => { const { rpc } = app.useContext>(); const login = async (values) => { const session = await rpc.signin(values); // Pronto! Cookies já estão setados automaticamente }; return
...
; }; const NavBar = () => { const { rpc } = app.useContext>(); const logout = async () => { await rpc.signout(); // Cookies já foram limpos automaticamente }; return ; }; ``` #### Por Que Isso é Revolucionário? 1. Zero Boilerplate - Sem gerenciamento manual de cookies - Sem necessidade de interceptors - Sem refresh tokens manual 2. Segurança por Padrão - Tokens são automaticamente encriptados - Cookies são seguros e httpOnly - Revalidação automática 3. Tipagem Completa - Payload do JWT é tipado - Validação com Zod integrada - Erros de autenticação tipados 4. Integração Perfeita ```tsx // Middleware que protege rotas const authMiddleware: App.Middleware = async (ctx) => { const session = await ctx.rpc.session.getSession(); if (!session) { throw new HttpError(401, 'Unauthorized'); } // Disponibiliza sessão para componentes ctx.store.set('session', session, 'public'); }; // Uso em rotas const router: App.Router = { routes: [ routerBuilder.routeGroup({ path: '/dashboard', middlewares: [authMiddleware], routes: [/*...*/] }) ] }; ``` ## O Store Compartilhado Uma das features mais poderosas do [React Edge](https://www.npmjs.com/package/react-edge) é sua capacidade de compartilhar estado entre worker e cliente de forma segura. Vamos ver como isso funciona: ```tsx // middleware/auth.ts const authMiddleware: App.Middleware = async (ctx) => { const token = ctx.request.headers.get('authorization'); if (!token) { throw new HttpError(401, 'Unauthorized'); } const user = await validateToken(token); // Dados públicos - automaticamente compartilhados com o cliente ctx.store.set('user', { id: user.id, name: user.name, role: user.role }, 'public'); // Dados privados - permanecem apenas no worker mas ctx.store.set('userSecret', user.secret); }; // components/Header.tsx const Header = () => { // Acesso transparente aos dados do store const { store } = app.useContext(); const user = store.get('user'); return (

Bem vindo, {user.name}!

{user.role === 'admin' && ( )}
); }; ``` ## Roteamento Elegante O sistema de rotas do [React Edge](https://www.npmjs.com/package/react-edge) é inspirado no [Hono](https://hono.dev/), mas com superpoderes para SSR: ```tsx const router: App.Router = { routes: [ routerBuilder.routeGroup({ path: '/dashboard', // Middlewares aplicados a todas rotas do grupo middlewares: [authMiddleware, dashboardMiddleware], routes: [ routerBuilder.route({ path: '/', handler: { page: { value: DashboardPage, // Headers específicos para esta rota headers: new Headers({ 'Cache-Control': 'private, max-age=0' }) } } }), routerBuilder.route({ path: '/api/stats', handler: { // Rotas podem retornar respostas diretas response: async (ctx) => { const stats = await ctx.rpc.stats.getDashboardStats(); return { value: Response.json(stats), // Cache por 5 minutos cache: { ttl: 300 } }; } } }) ] }) ] }; ``` ## Cache Distribuído com Edge Cache O [React Edge](https://www.npmjs.com/package/react-edge) possui um sistema de cache poderoso que funciona tanto para dados JSON quanto para páginas inteiras: ```tsx class ProductsAPI extends Rpc { async getProducts(category: string) { const products = await this.db.products.findByCategory(category); return this.createResponse(products, { cache: { ttl: 3600, // 1 hora tags: [`category:${category}`, 'products'] } }); } async updateProduct(id: string, data: ProductData) { await this.db.products.update(id, data); // Invalida cache específico do produto e sua categoria await this.cache.deleteBy({ tags: [ `product:${id}`, `category:${data.category}` ] }); } async searchProducts(query: string) { const results = await this.db.products.search(query); // Cache com prefixo para fácil invalidação return this.createResponse(results, { cache: { ttl: 300, tags: [`search:${query}`] } }); } } // Em qualquer lugar do código: await cache.deleteBy({ // Invalida todos resultados de busca keyPrefix: 'search:', // E todos produtos de uma categoria tags: ['category:electronics'] }); ``` ## Link: O Componente que Pensa à Frente O componente Link é uma solução inteligente e performática para pré-carregar recursos no lado cliente, garantindo uma navegação mais fluida e rápida para os usuários. Sua funcionalidade de prefetching é ativada ao passar o cursor sobre o link, aproveitando o momento de inatividade do usuário para requisitar antecipadamente os dados do destino. Como Funciona? 1. Prefetch Condicional: O atributo prefetch (ativo por padrão) controla se o pré-carregamento será realizado. 2. Cache Inteligente: Um conjunto (Set) é usado para armazenar os links já pré-carregados, evitando chamadas redundantes. 3. Mouse Enter: Quando o usuário passa o cursor sobre o link, a função handleMouseEnter verifica se o pré-carregamento é necessário e, caso positivo, inicia uma requisição fetch para o destino. 4. Erro Seguro: Qualquer falha na requisição é suprimida, garantindo que o comportamento do componente não seja afetado por erros momentâneos de rede. ```tsx Sobre Nós ``` Quando o usuário passar o mouse sobre o link “Sobre Nós”, o componente já começará a pré-carregar os dados da página /about, proporcionando uma transição quase instantânea. Idéia genial, não? Mas vi na [documentação do react.dev](https://react.dev/). ## app.useContext: O Portal para o Edge O `app.useContext` é o hook fundamental do [React Edge](https://www.npmjs.com/package/react-edge), proporcionando acesso a todo contexto do worker: ```tsx const DashboardPage = () => { const { // Parâmetros da rota atual pathParams, // Query params (já parseados) searchParams, // Rota que deu match path, // Rota original (com parâmetros) rawPath, // Proxy para RPC rpc, // Store compartilhado store, // URL completa url } = app.useContext>(); // Tipagem completa do RPC const { data } = app.useFetch( async (ctx) => ctx.rpc.getDashboardStats() ); // Acesso aos dados do store const user = store.get('user'); return (

Dashboard para {user.name}

Visualizando: {path}

); }; ``` ## app.useUrlState: Estado Sincronizado com a URL Mantenha o estado do seu app sincronizado com a URL de forma elegante: ```tsx const ProductsPage = () => { // Estado automaticamente sincronizado com query params const [filters, setFilters] = app.useUrlState({ // Chave na URL key: 'filters', // Valor inicial defaultValue: { category: 'all', minPrice: 0, maxPrice: 1000 }, // Validação opcional com Zod schema: filtersSchema }); const { data } = app.useFetch( async (ctx) => ctx.rpc.products.search(filters), { // Refetch quando filters mudar deps: [filters] } ); return (
{ // URL é atualizada automaticamente setFilters(newFilters); }} />
); }; ``` ## app.useStorageState: Estado Persistente Persista estado no localStorage/sessionStorage com tipagem: ```tsx const ThemeToggle = () => { // Estado persistido no localStorage const [theme, setTheme] = app.useStorageState({ key: 'theme', defaultValue: 'light', // localStorage ou sessionStorage storage: 'local', // Validação opcional validate: (value) => ['light', 'dark'].includes(value) }); // Versão com sessionStorage const [recentSearches, setRecentSearches] = app.useStorageState({ key: 'searches', defaultValue: [], storage: 'session' }); return ( ); }; ``` ## app.useDebounce: Controle de Frequência Debounce valores reativos com facilidade: ```tsx const SearchInput = () => { const [input, setInput] = useState(''); // Valor debounced atualiza apenas após 300ms de 'silêncio' const debouncedValue = app.useDebounce(input, 300); const { data } = app.useFetch( async (ctx) => ctx.rpc.search(debouncedValue), { // Fetch acontece apenas quando o valor debounced muda deps: [debouncedValue] } ); return (
setInput(e.target.value)} placeholder='Buscar...' />
); }; ``` ## app.useDistinct: Estado sem Duplicatas Mantenha arrays de valores únicos com tipagem: O `app.useDistinct` é um hook especializado em detectar quando um valor realmente mudou, com suporte a comparação profunda e debounce: ```tsx const SearchResults = () => { const [search, setSearch] = useState(''); // Detecta mudanças distintas no valor de busca const { value: currentSearch, // Valor atual prevValue: lastSearch, // Valor anterior distinct: hasChanged // Indica se houve mudança } = app.useDistinct(search, { // Debounce de 300ms debounce: 300, // Comparação profunda deep: true, // Função de comparação customizada compare: (a, b) => a?.toLowerCase() === b?.toLowerCase() }); // Fetch apenas quando a busca realmente mudar const { data } = app.useFetch( async (ctx) => ctx.rpc.search(currentSearch), { deps: [currentSearch], // Só executa se houve mudança distinta shouldFetch: () => hasChanged } ); return (
setSearch(e.target.value)} /> {hasChanged && ( Busca alterada de '{lastSearch}' para '{currentSearch}' )}
); }; ``` Os hooks do [React Edge](https://www.npmjs.com/package/react-edge) foram desenhados para trabalhar em harmonia, proporcionando uma experiência de desenvolvimento fluida e tipada. A combinação deles permite criar interfaces complexas e reativas com muito menos código. ## A CLI do React Edge: Potência na Ponta dos Dedos A CLI do [React Edge](https://www.npmjs.com/package/react-edge) foi projetada para simplificar a vida dos desenvolvedores, reunindo ferramentas essenciais em uma interface única e intuitiva. Seja você iniciante ou experiente, a CLI garante que você possa configurar, desenvolver, testar e implantar projetos com eficiência e sem complicações. Principais Recursos ### Comandos Modulares e Flexíveis: - build: Constrói tanto o app quanto o worker, com opções para especificar ambientes e modos de desenvolvimento ou produção. - dev: Inicia servidores de desenvolvimento locais ou remotos, permitindo trabalhar separadamente no app ou no worker. - deploy: Realiza deploys rápidos e eficientes utilizando o poder combinado do [Cloudflare Workers](https://workers.cloudflare.com/) e do [Cloudflare R2](https://www.cloudflare.com/developer-platform/products/r2/), garantindo performance e escalabilidade na infraestrutura edge. - logs: Monitora logs do worker diretamente no terminal. - lint: Automatiza a execução do Prettier e do ESLint, com suporte a correções automáticas. - test: Executa testes com cobertura opcional usando Vitest. - type-check: Valida a tipagem TypeScript no projeto. # Conclusão E assim, caros leitores, chegamos ao fim desta aventura pelo universo do [React Edge](https://www.npmjs.com/package/react-edge)! Sei que ainda há um mar de coisas incríveis para explorar, como as autenticações mais simples como Basic e Bearer, e outros segredinhos que fazem o dia a dia de um dev muito mais feliz. Mas calma lá! A ideia é trazer mais artigos detalhados no futuro para mergulhar de cabeça em cada uma dessas funcionalidades. E, spoiler: logo o [React Edge](https://www.npmjs.com/package/react-edge) será open source e devidamente documentado! Conciliar desenvolvimento, trabalho, escrever e um pouquinho de vida social não é fácil, mas a empolgação de ver essa maravilha em ação, especialmente com a velocidade absurda proporcionada pela infraestrutura da [Cloudflare](https://www.cloudflare.com/), é o combustível que me move. Então, segura a ansiedade, porque o melhor ainda está por vir! 🚀 Enquanto isso, se você quiser começar a explorar e testar agora mesmo, o pacote já está disponível no NPM: [React Edge no NPM.](https://www.npmjs.com/package/react-edge). Meu e-mail é feliperohdee@gmail.com, e estou sempre aberto a feedbacks, esta é só inicio desta jornada, sugestões e críticas construtivas. Se você gostou do que leu, compartilhe com seus amigos e colegas, e fique de olho nas novidades que estão por vir. Obrigado por me acompanhar até aqui, e até a próxima! 🚀🚀🚀