Este documento orienta você pelos casos de uso mais comuns do React Native WebView. Ele não cobre a API completa, mas depois de lê-lo e ver os trechos de código de exemplo, você deve ter uma boa noção de como o WebView funciona e os padrões comuns para usar o WebView.
Este guia atualmente é um trabalho em andamento.
- HTML Básico
- Url
- Carregando de arquivos HTML locais
- Controlando as alterações do estado de navegação
- Adicionar suporte para upload de arquivos
- Upload de vários arquivos
- Adicionar suporte para download de arquivos
- Comunicação entre JS e Native
- Trabalhando com cabeçalhos, sessões e cookies personalizados
A maneira mais simples de usar a WebView é simplesmente canalizar o HTML que você deseja exibir. Observe que definir uma fonte html
requer que a propriedade originWhiteList seja definida como ['*']
.
import React, { Component } from 'react';
import { WebView } from 'react-native-webview';
class MyInlineWeb extends Component {
render() {
return (
<WebView
originWhitelist={['*']}
source={{ html: '<h1>Esta é uma fonte HTML estática!</h1>' }}
/>
);
}
}
Passar uma nova fonte html estática fará com que o WebView seja renderizado novamente.
Este é o caso de uso mais comum para WebView.
import React, { Component } from 'react';
import { WebView } from 'react-native-webview';
class MyWeb extends Component {
render() {
return <WebView source={{ uri: 'https://reactnative.dev/' }} />;
}
}
Observação: no momento, isso não está funcionando conforme discutido em #428 e #518. As possíveis soluções incluem agrupar todos os ativos com webpack ou similar, ou executar um servidor da web local.
Mostrar método que não funciona
Às vezes, você teria agrupado um arquivo HTML junto com o aplicativo e gostaria de carregar o ativo HTML em seu WebView. Para fazer isso no iOS e no Windows, basta importar o arquivo html como qualquer outro ativo, conforme mostrado abaixo.
import React, { Component } from 'react';
import { WebView } from 'react-native-webview';
const myHtmlFile = require('./my-asset-folder/local-site.html');
class MyWeb extends Component {
render() {
return <WebView source={myHtmlFile} />;
}
}
No entanto, no Android, você precisa colocar o arquivo HTML dentro do diretório de ativos do seu projeto Android. Por exemplo, se local-site.html
for seu arquivo HTML e você quiser carregá-lo na visualização da web, você deve mover o arquivo para o diretório de ativos do Android do seu projeto, que é your-project/android/app/src /main/assets/
. Então você pode carregar o arquivo html conforme mostrado no seguinte bloco de código
import React, { Component } from 'react';
import { WebView } from 'react-native-webview';
class MyWeb extends Component {
render() {
return (
<WebView source={{ uri: 'file:///android_asset/local-site.html' }} />
);
}
}
Às vezes, você deseja interceptar um usuário tocando em um link em sua visualização da web e fazer algo diferente de navegar na visualização da web. Aqui está um código de exemplo de como você pode fazer isso usando a função onNavigationStateChange
.
import React, { Component } from 'react';
import { WebView } from 'react-native-webview';
class MyWeb extends Component {
webview = null;
render() {
return (
<WebView
ref={(ref) => (this.webview = ref)}
source={{ uri: 'https://reactnative.dev/' }}
onNavigationStateChange={this.handleWebViewNavigationStateChange}
/>
);
}
handleWebViewNavigationStateChange = (newNavState) => {
// newNavState se parece com isso:
// {
// url?: string;
// title?: string;
// loading?: boolean;
// canGoBack?: boolean;
// canGoForward?: boolean;
// }
const { url } = newNavState;
if (!url) return;
// lidar com doctypes
if (url.includes('.pdf')) {
this.webview.stopLoading();
// abra um modal com o visualizador de PDF
}
// uma maneira de lidar com um envio de formulário bem-sucedido é por meio de strings de consulta
if (url.includes('?message=success')) {
this.webview.stopLoading();
// talvez fechar?
}
// uma maneira de lidar com erros é via string de consulta
if (url.includes('?errors=true')) {
this.webview.stopLoading();
}
// redirecionar para outro lugar
if (url.includes('google.com')) {
const newURL = 'https://reactnative.dev/';
const redirectTo = 'window.location = "' + newURL + '"';
this.webview.injectJavaScript(redirectTo);
}
};
}
Para iOS, tudo que você precisa fazer é especificar as permissões em seu arquivo ios/[project]/Info.plist
:
Captura de foto:
<key>NSCameraUsageDescription</key>
<string>Take pictures for certain activities</string>
Seleção de galeria:
<key>NSPhotoLibraryUsageDescription</key>
<string>Select pictures for certain activities</string>
Gravação de vídeo:
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for recording videos</string>
Adicione permissão no AndroidManifest.xml:
<manifest ...>
......
<!-- isso é necessário apenas para Android 4.1-5.1 (api 16-22) -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
......
</manifest>
Se a entrada do arquivo indicar que imagens ou vídeo são desejados com accept
, então a WebView tentará fornecer opções ao usuário para usar sua câmera para tirar uma foto ou vídeo.
Normalmente, os aplicativos que não têm permissão para usar a câmera podem solicitar que o usuário use um aplicativo externo para que o aplicativo solicitante não precise de permissão. No entanto, o Android fez uma exceção especial para isso em torno da câmera para reduzir a confusão dos usuários. Se um aplicativo can solicitar a permissão da câmera porque foi declarado e o usuário não concedeu a permissão, ele não poderá disparar um intent que usaria a câmera (MediaStore.ACTION_IMAGE_CAPTURE
ou MediaStore.ACTION_VIDEO_CAPTURE
). Nesse cenário, cabe ao desenvolvedor solicitar a permissão da câmera antes que um upload de arquivo diretamente usando a câmera seja necessário.
O upload de arquivo usando <input type="file" />
não é compatível com o Android 4.4 KitKat (consulte os detalhes):
import { WebView } from "react-native-webview";
WebView.isFileUploadSupported().then(res => {
if (res === true) {
// o upload de arquivos é suportado
} else {
// o upload de arquivos não é suportado
}
});
Você pode controlar a seleção de arquivo único ou múltiplo especificando o múltiplo
em seu elemento input
:
// seleção de vários arquivos
<input type="file" multiple />
// seleção de arquivo único
<input type="file" />
O iOS, você terá que fornecer seu próprio código para baixar os arquivos. Você pode fornecer um retorno de chamada onFileDownload
para o componente WebView como um prop. Se o RNCWebView determinar que um download de arquivo precisa ocorrer, a URL onde você pode baixar o arquivo será dado a onFileDownload
. A partir desse retorno de chamada, você pode baixar esse arquivo da maneira que desejar.
NOTA: O iOS 13+ é necessário para a melhor experiência de download possível. No iOS 13, a Apple adicionou uma API para acessar cabeçalhos de resposta HTTP, que
é usado para determinar se uma resposta HTTP deve ser um download. No iOS 12 ou anterior, apenas os tipos MIME que não podem ser renderizados pela webview serão
acionar chamadas para onFileDownload
.
Exemplo:
onFileDownload = ({ nativeEvent }) => {
const { downloadUrl } = nativeEvent;
// --> Seu código de download vai aqui <--
};
Para poder salvar imagens na galeria você precisa especificar esta permissão em seu arquivo ios/[project]/Info.plist
:
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Save pictures for certain activities.</string>
No Android, a integração com o DownloadManager é integrada. Adicione esta permissão em AndroidManifest.xml (necessário apenas se seu aplicativo for compatível com versões do Android anteriores a 10):
<manifest ...>
......
<!-- isso é necessário para salvar arquivos em versões do Android inferiores a 10 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
......
</manifest>
Muitas vezes você vai querer enviar mensagens para as páginas da web carregadas por suas visualizações da web e também receber mensagens de volta dessas páginas da web.
Para fazer isso, o React Native WebView expõe três opções diferentes:
- Reagir Nativo -> Web: A prop
injectedJavaScript
- Reagir Nativo -> Web: O método
injectJavaScript
- Web -> React Native: O método
postMessage
e a proponMessage
Este é um script que é executado imediatamente após o carregamento da página da Web pela primeira vez. Ele é executado apenas uma vez, mesmo que a página seja recarregada ou navegada.
import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
export default class App extends Component {
render() {
const runFirst = `
document.body.style.backgroundColor = 'red';
setTimeout(function() { window.alert('hi') }, 2000);
true; // nota: isso é necessário, ou às vezes você terá falhas
`;
return (
<View style={{ flex: 1 }}>
<WebView
source={{
uri: 'https://github.com/react-native-webview/react-native-webview',
}}
onMessage={(event) => {}}
injectedJavaScript={runFirst}
/>
</View>
);
}
}
Isso executa o JavaScript na string runFirst
assim que a página é carregada. Nesse caso, você pode ver que o estilo do corpo foi alterado para vermelho e o alerta apareceu após 2 segundos. Um evento onMessage
também é necessário para injetar o código JavaScript no WebView.
Ao definir injectedJavaScriptForMainFrameOnly: false
, a injeção de JavaScript ocorrerá em todos os quadros (não apenas no quadro principal) se houver suporte para a plataforma especificada. Por exemplo, se uma página contiver um iframe, o javascript também será injetado nesse iframe com este definido como false
. (Observe que isso não é suportado no Android.) Há também o injectedJavaScriptBeforeContentLoadedForMainFrameOnly
para injeção antes do carregamento do conteúdo. Leia mais sobre isso na Referência.
Como funciona no OS
No iOS,
– isso não é mais verdade a partir da versãoinjectedJavaScript
executa um método no WebView chamadoevaluateJavaScript:completionHandler:
8.2.0
. Em vez disso, usamos umWKUserScript
com tempo de injeçãoWKUserScriptInjectionTimeAtDocumentEnd
. Como consequência, oinjectedJavaScript
não retorna mais um valor de avaliação nem registra um aviso no console. No caso improvável de seu aplicativo depender desse comportamento, consulte as etapas de migração aqui para manter comportamento equivalente. No Android,injectedJavaScript
executa um método no Android WebView chamadoevaluateJavascriptWithFallback
No Windows, oinjectedJavaScript
executa um método no WinRT/C++ WebView chamadoInvokeScriptAsync
Este é um script executado antes do carregamento da página da Web pela primeira vez. Ele é executado apenas uma vez, mesmo que a página seja recarregada ou navegada. Isso é útil se você deseja injetar qualquer coisa na janela, armazenamento local ou documento antes da execução do código da web.
import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
export default class App extends Component {
render() {
const runFirst = `
window.isNativeApp = true;
true; // nota: isso é necessário, ou às vezes você terá falhas
`;
return (
<View style={{ flex: 1 }}>
<WebView
source={{
uri: 'https://github.com/react-native-webview/react-native-webview',
}}
injectedJavaScriptBeforeContentLoaded={runFirst}
/>
</View>
);
}
}
Isso executa o JavaScript na string runFirst
antes que a página seja carregada. Nesse caso, o valor de window.isNativeApp
será definido como true antes que o código da web seja executado.
Ao definir injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false
, a injeção de JavaScript ocorrerá em todos os quadros (não apenas no quadro superior) se houver suporte para a plataforma especificada. No entanto, embora o suporte para injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false
tenha sido implementado para iOS e macOS, não está claro que é realmente possível injetar JS em iframes neste ponto do ciclo de vida da página e, portanto, confiar no comportamento esperado dessa prop quando definido como false
não é recomendado.
No iOS,
– isso não é mais verdade a partir da versãoinjectedJavaScriptBeforeContentLoaded
executa um método no WebView chamadoevaluateJavaScript:completionHandler:
8.2.0
. Em vez disso, usamos umWKUserScript
com tempo de injeçãoWKUserScriptInjectionTimeAtDocumentStart
. Como consequência,injectedJavaScriptBeforeContentLoaded
não retorna mais um valor de avaliação nem registra um aviso no console. No caso improvável de seu aplicativo depender desse comportamento, consulte as etapas de migração aqui para manter comportamento equivalente. No Android,injectedJavaScript
executa um método no Android WebView chamadoevaluateJavascriptWithFallback
Observação sobre compatibilidade com Android: para aplicativos direcionados aBuild.VERSION_CODES.N
ou posterior, o estado JavaScript de um WebView vazio não é mais mantido em navegações comoloadUrl(java.lang.String)
. Por exemplo, variáveis globais e funções definidas antes de chamarloadUrl(java.lang.String)
não existirão na página carregada. Os aplicativos devem usar a API nativa do AndroidaddJavascriptInterface(Object, String)
para persistir objetos JavaScript nas navegações.
Embora conveniente, a desvantagem da prop injectedJavaScript
mencionado anteriormente é que ele é executado apenas uma vez. É por isso que também expomos um método no webview ref chamado injectJavaScript
(observe o nome ligeiramente diferente!).
import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
export default class App extends Component {
render() {
const run = `
document.body.style.backgroundColor = 'blue';
true;
`;
setTimeout(() => {
this.webref.injectJavaScript(run);
}, 3000);
return (
<View style={{ flex: 1 }}>
<WebView
ref={(r) => (this.webref = r)}
source={{
uri: 'https://github.com/react-native-webview/react-native-webview',
}}
/>
</View>
);
}
}
Após 3 segundos, este código torna o fundo azul:
Como funciona no OS
No iOS,
injectJavaScript
chamaevaluateJS:andThen:
da WebView No Android,injectJavaScript
chama o métodoevaluateJavascriptWithFallback
do Android WebView
Ser capaz de enviar JavaScript para a página da Web é ótimo, mas e quando a página da Web deseja se comunicar de volta ao seu código React Native? É aqui que entram o window.ReactNativeWebView.postMessage
e a prop onMessage
.
Você deve definir onMessage
ou o método window.ReactNativeWebView.postMessage
não será injetado na página web.
window.ReactNativeWebView.postMessage
só aceita um argumento que deve ser uma string.
import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
export default class App extends Component {
render() {
const html = `
<html>
<head></head>
<body>
<script>
setTimeout(function () {
window.ReactNativeWebView.postMessage("Hello!")
}, 2000)
</script>
</body>
</html>
`;
return (
<View style={{ flex: 1 }}>
<WebView
source={{ html }}
onMessage={(event) => {
alert(event.nativeEvent.data);
}}
/>
</View>
);
}
}
Este código resultará neste alerta:
No React Native WebView, você pode definir um cabeçalho personalizado como este:
<WebView
source={{
uri: 'http://example.com',
headers: {
'my-custom-header-key': 'my-custom-header-value',
},
}}
/>
Isso definirá o cabeçalho no primeiro carregamento, mas não nas navegações de página subsequentes.
Para contornar isso, você pode rastrear o URL atual, interceptar novos carregamentos de página e navegar até eles você mesmo (crédito original desta técnica para Chirag Shah do Big Binary):
const CustomHeaderWebView = (props) => {
const { uri, onLoadStart, ...restProps } = props;
const [currentURI, setURI] = useState(props.source.uri);
const newSource = { ...props.source, uri: currentURI };
return (
<WebView
{...restProps}
source={newSource}
onShouldStartLoadWithRequest={(request) => {
// Se estivermos carregando o URI atual, permita que ele carregue
if (request.url === currentURI) return true;
// Estamos carregando um novo URL -- altere o estado primeiro
setURI(request.url);
return false;
}}
/>
);
};
<CustomHeaderWebView
source={{
uri: 'http://example.com',
headers: {
'my-custom-header-key': 'my-custom-header-value',
},
}}
/>;
Você pode definir cookies no lado React Native usando o pacote @react-native-cookies/cookies.
Ao fazer isso, você provavelmente desejará habilitar a propriedade sharedCookiesEnabled também.
const App = () => {
return (
<WebView
source={{ uri: 'http://example.com' }}
sharedCookiesEnabled={true}
/>
);
};
Se você deseja enviar cookies personalizados na própria WebView, pode fazê-lo em um cabeçalho personalizado, como este:
const App = () => {
return (
<WebView
source={{
uri: 'http://example.com',
headers: {
Cookie: 'cookie1=asdf; cookie2=dfasdfdas',
},
}}
sharedCookiesEnabled={true}
/>
);
};
Observe que esses cookies só serão enviados na primeira solicitação, a menos que você use a técnica acima para definir cabeçalhos personalizados em cada carregamento de página.
Existem algumas inconsistências na forma como o interruptor de silêncio de hardware é tratado entre os elementos audio
e video
incorporados e entre as plataformas iOS e Android.
O áudio no iOS
será silenciado quando a chave de silêncio do hardware estiver na posição ligada, a menos que o parâmetro ignoreSilentHardwareSwitch
seja definido como verdadeiro.
O vídeo no iOS
sempre ignorará a chave de silêncio do hardware.
Esse arquivo está disponível em: