Gerenciando o bridge e o adapter
Esta página descreve várias formas de gerenciar o bridge no lado WebView e o createFoxRNAdapter no lado React Native. Escolha o padrão que melhor se encaixa no ciclo de vida da sua aplicação.
---
Índice
- 1. Lado Web - Bridge
- 1.1 Bridge global (uma vez, sem cleanup)
- 1.2 Bridge global com dispose no unmount
- 1.3 Bridge por componente (dentro do useEffect)
- 2. Lado RN - Adapter
- 2.1 Adapter no componente da WebView
- 2.2 Adapter global (setFox / getFox)
- 2.3 Hook useFox que usa o global
- 2.4 Provider (Context)
- 3. Resumo
---
1. Lado Web — Bridge
createReactNativeBridge(options) retorna uma função dispose() que remove o listener de message e o middleware de emit. Você pode usar o bridge de três formas.
1.1 Bridge global (uma vez, sem cleanup)
Inicialize uma vez em um módulo importado no entry point. O bridge fica ativo durante toda a vida da aplicação. Ideal quando a WebView não é desmontada ou quando você não precisa liberar recursos.
Módulo src/bridge.ts:
import { createReactNativeBridge } from "fox-events/bridge-react-native";
createReactNativeBridge({
direction: "both",
filter: (name) => name.startsWith("app:"),
debug: false,
});Entry point (main.tsx): importe antes do App:
import "./bridge";
import App from "./App";Qualquer componente pode usar Fox.emit e Fox.on sem se preocupar com setup ou cleanup.
---
1.2 Bridge global com dispose no unmount
Mesma ideia de “bridge uma vez”, mas você exporta o dispose e chama no cleanup do componente raiz. Assim, quando a árvore React desmontar (ex.: usuário sai da tela que contém a WebView), o bridge é desmontado e não fica ouvindo message nem enviando para o RN.
Módulo src/bridge.ts:
import { createReactNativeBridge } from "fox-events/bridge-react-native";
/**
* Inicializa o bridge RN ↔ WebView uma vez.
* Para desmontar (ex.: quando o componente raiz desmontar), chame disposeBridge()
* no cleanup do useEffect: return () => disposeBridge();
*/
export const disposeBridge = createReactNativeBridge({
direction: "both",
filter: (name) => name.startsWith("app:"),
debug: false,
});Entry point: importe o módulo antes do App (o bridge já sobe na carga).
No componente raiz (ex.: App.tsx):
import { useEffect } from "react";
import { disposeBridge } from "./bridge";
function App() {
useEffect(() => {
// ... seus Fox.on, etc. ...
return () => disposeBridge();
}, []);
return (/* ... */);
}Quando o componente desmontar, disposeBridge() remove o listener e o middleware. Use este padrão quando a WebView pode ser desmontada e você quer evitar listeners órfãos.
---
1.3 Bridge por componente (dentro do useEffect)
Crie e destrua o bridge por componente: no useEffect você chama createReactNativeBridge e no cleanup chama o dispose retornado. O bridge só existe enquanto o componente estiver montado.
import { useEffect } from "react";
import { Fox } from "fox-events";
import { createReactNativeBridge } from "fox-events/bridge-react-native";
function WebViewScreen() {
useEffect(() => {
const dispose = createReactNativeBridge({
direction: "both",
filter: (name) => name.startsWith("app:"),
debug: false,
});
Fox.on("app:command", (payload) => {
/* ... */
});
return () => dispose();
}, []);
return (/* ... */);
}Use quando o bridge estiver amarrado à vida de um único componente (ex.: uma tela que monta/desmonta a WebView) e você não quiser um bridge global.
---
2. Lado RN — Adapter
createFoxRNAdapter({ sendToWebView, filter? }) retorna um adapter que você usa com fox.emit, fox.on, fox.handleMessage. Abaixo, quatro formas de gerenciar esse adapter.
2.1 Adapter no componente da WebView
Crie o adapter com useMemo no mesmo componente que renderiza a WebView e passe a ref para sendToWebView. O adapter vive apenas nesse componente; para usar em filhos, passe fox por props ou por Context.
import { useMemo, useRef } from "react";
import { WebView } from "react-native-webview";
import { createFoxRNAdapter } from "fox-events/bridge-react-native";
function App() {
const webViewRef = useRef<WebView>(null);
const fox = useMemo(
() =>
createFoxRNAdapter({
sendToWebView: (msg) => {
const code = `window.postMessage(${JSON.stringify(msg)}, '*'); true;`;
webViewRef.current?.injectJavaScript(code);
},
filter: (name) => name.startsWith("app:"),
}),
[]
);
return (
<WebView
ref={webViewRef}
source={{ uri: "https://example.com" }}
onMessage={(e) => fox.handleMessage(e.nativeEvent.data)}
/>
);
}Ideal quando só esse componente (e talvez filhos via props) precisa do fox.
---
2.2 Adapter global (setFox / getFox)
Registre o adapter em um módulo global após criá-lo (ex.: no layout que tem a WebView). Em qualquer tela ou módulo, obtenha o adapter com getFox() e use fox.emit / fox.on sem Provider.
Módulo app/fox-global.ts:
import type { FoxRNAdapter } from "fox-events/bridge-react-native";
let foxInstance: FoxRNAdapter | null = null;
export function setFox(fox: FoxRNAdapter): void {
foxInstance = fox;
}
export function getFox(): FoxRNAdapter {
if (foxInstance == null) {
throw new Error(
"Fox not initialized. Ensure the layout with WebView has mounted and called setFox(fox)."
);
}
return foxInstance;
}No layout/componente que tem a WebView: crie o adapter com useMemo e registre com setFox no useEffect:
const fox = useMemo(
() =>
createFoxRNAdapter({
sendToWebView: (msg) => {
const code = `window.postMessage(${JSON.stringify(msg)}, '*'); true;`;
webViewRef.current?.injectJavaScript(code);
},
}),
[]
);
useEffect(() => {
setFox(fox);
}, [fox]);Em qualquer outra tela ou módulo:
import { getFox } from "@/app/fox-global";
function OutraTela() {
const fox = getFox();
const handleEnviar = () => fox.emit("app:action", { id: "1" });
return (/* ... */);
}Assim você usa o mesmo adapter em várias telas sem passar props nem Provider.
---
2.3 Hook useFox que usa o global
Encapsule o getFox() em um hook para manter a API consistente e evitar chamar getFox() em todo lugar.
app/fox-context.tsx (ou similar):
import type { FoxRNAdapter } from "fox-events/bridge-react-native";
import { getFox } from "./fox-global";
export function useFox(): FoxRNAdapter {
return getFox();
}Uso: const fox = useFox(); em qualquer componente. Por baixo continua usando o global (getFox), então não precisa de Provider.
---
2.4 Provider (Context)
Se preferir injetar o adapter pela árvore React (evitando módulo global), crie um Context e forneça o adapter no layout que tem a WebView.
import { createContext, useContext, useMemo, useRef } from "react";
import { WebView } from "react-native-webview";
import { createFoxRNAdapter } from "fox-events/bridge-react-native";
const FoxContext = createContext<ReturnType<typeof createFoxRNAdapter> | null>(null);
export function useFox() {
const fox = useContext(FoxContext);
if (fox == null) throw new Error("useFox must be used within FoxProvider");
return fox;
}
function App() {
const webViewRef = useRef<WebView>(null);
const fox = useMemo(
() =>
createFoxRNAdapter({
sendToWebView: (msg) => {
const code = `window.postMessage(${JSON.stringify(msg)}, '*'); true;`;
webViewRef.current?.injectJavaScript(code);
},
}),
[]
);
return (
<FoxContext.Provider value={fox}>
<WebView
ref={webViewRef}
onMessage={(e) => fox.handleMessage(e.nativeEvent.data)}
source={{ uri: "https://example.com" }}
/>
</FoxContext.Provider>
);
}Use quando quiser evitar estado global e manter o adapter apenas na subárvore que envolver com FoxProvider.
---
3. Resumo
| Lado | Padrão | Quando usar |
|---|---|---|
| Web | Bridge global (sem cleanup) | WebView não desmonta ou não precisa liberar recursos. |
| Web | Bridge global + disposeBridge() no cleanup | WebView pode desmontar; você quer remover listeners ao sair da tela. |
| Web | Bridge no useEffect (dispose no return) | Bridge atrelado ao ciclo de vida de um único componente. |
| RN | Adapter no componente | Só o componente da WebView (e filhos via props) usa o adapter. |
| RN | Global (setFox / getFox) | Várias telas usam o mesmo adapter sem props nem Provider. |
| RN | Hook useFox() (usando global) | Mesmo que o global, com API const fox = useFox();. |
| RN | Provider (Context) | Preferir injeção pela árvore em vez de módulo global. |
Para mais detalhes de configuração e exemplos de eventos, veja Bridge React Native.