Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Componentes Nativos
Si deseas crear nuevos Componentes de React Native que encapsulen un Componente Host como un tipo único de CheckBox en Android o un UIButton en iOS, deberás usar un Componente Nativo de Fabric.
Esta guía te mostrará cómo construir Componentes Nativos de Fabric implementando un componente de vista web. Los pasos para hacerlo son:
-
Definir una especificación en JavaScript usando Flow o TypeScript.
-
Configurar el sistema de gestión de dependencias para generar código a partir de la especificación y vincularlo automáticamente.
-
Implementar el código nativo.
-
Utilizar el componente en una aplicación.
Necesitarás una aplicación generada con una plantilla básica para usar el componente:
npx @react-native-community/cli@latest init Demo --install-pods false
Creación de un Componente WebView
Esta guía te mostrará cómo crear un componente WebView. Lo construiremos usando el componente WebView de Android y el componente WKWebView de iOS.
Comencemos creando la estructura de carpetas para alojar el código de nuestro componente:
mkdir -p Demo/{specs,android/app/src/main/java/com/webview}
Esto te dará la siguiente estructura donde trabajarás:
Demo
├── android/app/src/main/java/com/webview
└── ios
└── specs
-
La carpeta
android/app/src/main/java/com/webviewcontendrá nuestro código de Android. -
La carpeta
ioscontendrá nuestro código de iOS. -
La carpeta
specscontendrá el archivo de especificación de Codegen.
1. Definir la especificación para Codegen
Tu especificación debe definirse en TypeScript o Flow (consulta la documentación de Codegen para más detalles). Esto lo utiliza Codegen para generar el código C++, Objective-C++ y Java que conecta tu código de plataforma con el entorno de ejecución JavaScript donde funciona React.
El archivo de especificación debe nombrarse <MODULE_NAME>NativeComponent.{ts|js} para funcionar con Codegen. El sufijo NativeComponent no es solo una convención, sino que realmente lo utiliza Codegen para detectar archivos de especificación.
Usa esta especificación para nuestro componente WebView:
- TypeScript
- Flow
import type {
CodegenTypes,
HostComponent,
ViewProps,
} from 'react-native';
import {codegenNativeComponent} from 'react-native';
type WebViewScriptLoadedEvent = {
result: 'success' | 'error';
};
export interface NativeProps extends ViewProps {
sourceURL?: string;
onScriptLoaded?: CodegenTypes.BubblingEventHandler<WebViewScriptLoadedEvent> | null;
}
export default codegenNativeComponent<NativeProps>(
'CustomWebView',
) as HostComponent<NativeProps>;
// @flow strict-local
import type {CodegenTypes, HostComponent, ViewProps} from 'react-native';
import {codegenNativeComponent} from 'react-native';
type WebViewScriptLoadedEvent = $ReadOnly<{|
result: "success" | "error",
|}>;
type NativeProps = $ReadOnly<{|
...ViewProps,
sourceURL?: string;
onScriptLoaded?: CodegenTypes.BubblingEventHandler<WebViewScriptLoadedEvent>?;
|}>;
export default (codegenNativeComponent<NativeProps>(
'CustomWebView',
): HostComponent<NativeProps>);
Esta especificación se compone de tres partes principales, excluyendo las importaciones:
-
WebViewScriptLoadedEventes un tipo de dato auxiliar para la información que el evento necesita pasar de nativo a JavaScript. -
NativePropses la definición de las props que podemos establecer en el componente. -
La declaración
codegenNativeComponentnos permite generar el código del componente personalizado y define un nombre para el componente que se utilizará para vincularlo con las implementaciones nativas.
Al igual que con los Módulos Nativos, puedes tener múltiples archivos de especificación en el directorio specs/. Para más información sobre los tipos que puedes usar y cómo se mapean a tipos de plataforma, consulta el apéndice.
2. Configurar la ejecución de Codegen
La especificación es utilizada por las herramientas Codegen de React Native para generar interfaces específicas de plataforma y código repetitivo. Para esto, Codegen necesita saber dónde encontrar nuestra especificación y qué hacer con ella. Actualiza tu package.json para incluir:
"start": "react-native start",
"test": "jest"
},
"codegenConfig": {
"name": "AppSpec",
"type": "components",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.webview"
},
"ios": {
"componentProvider": {
"CustomWebView": "RCTWebView"
}
}
},
"dependencies": {
Con todo configurado para Codegen, necesitamos preparar nuestro código nativo para conectarse con el código generado.
Ten en cuenta que para iOS, estamos mapeando declarativamente el nombre del componente JS exportado por la especificación (CustomWebView) con la clase iOS que implementará el componente de forma nativa.
2. Construyendo tu código nativo
Ahora es momento de escribir el código nativo de la plataforma para que, cuando React requiera renderizar una vista, la plataforma pueda crear la vista nativa correcta y mostrarla en pantalla.
Debes trabajar en ambas plataformas: Android e iOS.
Esta guía te muestra cómo crear un componente nativo que solo funciona con la Nueva Arquitectura. Si necesitas soportar tanto la Nueva Arquitectura como la Arquitectura Heredada, consulta nuestra guía de compatibilidad hacia atrás.
- Android
- iOS
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Ahora es momento de escribir código para la plataforma Android para poder renderizar la vista web. Los pasos que debes seguir son:
-
Ejecutar Codegen
-
Escribir el código para
ReactWebView -
Escribir el código para
ReactWebViewManager -
Escribir el código para
ReactWebViewPackage -
Registrar
ReactWebViewPackageen la aplicación
1. Ejecutar Codegen mediante Gradle
Ejecuta esto una vez para generar plantillas que tu IDE pueda utilizar.
cd android
./gradlew generateCodegenArtifactsFromSchema
Codegen generará la interfaz ViewManager que debes implementar y el delegado ViewManager para la vista web.
2. Escribir ReactWebView
ReactWebView es el componente que envuelve la vista nativa de Android que React Native renderizará al usar nuestro componente personalizado.
Crea un archivo ReactWebView.java o ReactWebView.kt en la carpeta android/src/main/java/com/webview con este código:
- Java
- Kotlin
package com.webview;
import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.events.Event;
public class ReactWebView extends WebView {
public ReactWebView(Context context) {
super(context);
configureComponent();
}
public ReactWebView(Context context, AttributeSet attrs) {
super(context, attrs);
configureComponent();
}
public ReactWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
configureComponent();
}
private void configureComponent() {
this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
this.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
emitOnScriptLoaded(OnScriptLoadedEventResult.success);
}
});
}
public void emitOnScriptLoaded(OnScriptLoadedEventResult result) {
ReactContext reactContext = (ReactContext) context;
int surfaceId = UIManagerHelper.getSurfaceId(reactContext);
EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId());
WritableMap payload = Arguments.createMap();
payload.putString("result", result.name());
OnScriptLoadedEvent event = new OnScriptLoadedEvent(surfaceId, getId(), payload);
if (eventDispatcher != null) {
eventDispatcher.dispatchEvent(event);
}
}
public enum OnScriptLoadedEventResult {
success,
error
}
private class OnScriptLoadedEvent extends Event<OnScriptLoadedEvent> {
private final WritableMap payload;
OnScriptLoadedEvent(int surfaceId, int viewId, WritableMap payload) {
super(surfaceId, viewId);
this.payload = payload;
}
@Override
public String getEventName() {
return "onScriptLoaded";
}
@Override
public WritableMap getEventData() {
return payload;
}
}
}
package com.webview
import android.content.Context
import android.util.AttributeSet
import android.webkit.WebView
import android.webkit.WebViewClient
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.events.Event
class ReactWebView: WebView {
constructor(context: Context) : super(context) {
configureComponent()
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
configureComponent()
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
configureComponent()
}
private fun configureComponent() {
this.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
this.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
emitOnScriptLoaded(OnScriptLoadedEventResult.success)
}
}
}
fun emitOnScriptLoaded(result: OnScriptLoadedEventResult) {
val reactContext = context as ReactContext
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
val payload =
Arguments.createMap().apply {
putString("result", result.name)
}
val event = OnScriptLoadedEvent(surfaceId, id, payload)
eventDispatcher?.dispatchEvent(event)
}
enum class OnScriptLoadedEventResult {
success,
error;
}
inner class OnScriptLoadedEvent(
surfaceId: Int,
viewId: Int,
private val payload: WritableMap
) : Event<OnScriptLoadedEvent>(surfaceId, viewId) {
override fun getEventName() = "onScriptLoaded"
override fun getEventData() = payload
}
}
ReactWebView extiende el WebView de Android, permitiéndote reutilizar fácilmente todas las propiedades ya definidas por la plataforma.
La clase define los tres constructores de Android pero delega su implementación real a la función privada configureComponent. Esta función se encarga de inicializar todas las propiedades específicas del componente: en este caso configuras el diseño del WebView y defines el WebClient para personalizar el comportamiento del WebView. En este código, ReactWebView emite un evento cuando la página termina de cargar, implementando el método onPageFinished de WebClient.
El código define luego una función auxiliar para emitir eventos. Para emitir un evento, debes:
-
obtener una referencia al
ReactContext; -
recuperar el
surfaceIdde la vista que estás presentando; -
obtener una referencia al
eventDispatcherasociado con la vista; -
construir la carga útil del evento usando un objeto
WritableMap; -
crear el objeto de evento que necesitas enviar a JavaScript;
-
llamar a
eventDispatcher.dispatchEventpara enviar el evento.
La última parte del archivo contiene la definición de los tipos de datos necesarios para enviar el evento:
-
OnScriptLoadedEventResult, con los posibles resultados del eventoOnScriptLoaded. -
El evento
OnScriptLoadedEventen sí, que debe extender la claseEventde React Native.
3. Escribir WebViewManager
WebViewManager es la clase que conecta el entorno de ejecución de React Native con la vista nativa.
Cuando React recibe la instrucción desde la app para renderizar un componente específico, utiliza el administrador de vistas registrado para crear la vista y pasar todas las propiedades requeridas.
Este es el código de ReactWebViewManager.
- Java
- Kotlin
package com.webview;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerDelegate;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.viewmanagers.CustomWebViewManagerInterface;
import com.facebook.react.viewmanagers.CustomWebViewManagerDelegate;
import java.util.HashMap;
import java.util.Map;
@ReactModule(name = ReactWebViewManager.REACT_CLASS)
class ReactWebViewManager extends SimpleViewManager<ReactWebView> implements CustomWebViewManagerInterface<ReactWebView> {
private final CustomWebViewManagerDelegate<ReactWebView, ReactWebViewManager> delegate =
new CustomWebViewManagerDelegate<>(this);
@Override
public ViewManagerDelegate<ReactWebView> getDelegate() {
return delegate;
}
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public ReactWebView createViewInstance(ThemedReactContext context) {
return new ReactWebView(context);
}
@ReactProp(name = "sourceUrl")
@Override
public void setSourceURL(ReactWebView view, String sourceURL) {
if (sourceURL == null) {
view.emitOnScriptLoaded(ReactWebView.OnScriptLoadedEventResult.error);
return;
}
view.loadUrl(sourceURL, new HashMap<>());
}
public static final String REACT_CLASS = "CustomWebView";
@Override
public Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
Map<String, Object> map = new HashMap<>();
Map<String, Object> bubblingMap = new HashMap<>();
bubblingMap.put("phasedRegistrationNames", new HashMap<String, String>() {{
put("bubbled", "onScriptLoaded");
put("captured", "onScriptLoadedCapture");
}});
map.put("onScriptLoaded", bubblingMap);
return map;
}
}
package com.webview
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerDelegate;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.viewmanagers.CustomWebViewManagerInterface;
import com.facebook.react.viewmanagers.CustomWebViewManagerDelegate;
@ReactModule(name = ReactWebViewManager.REACT_CLASS)
class ReactWebViewManager(context: ReactApplicationContext) : SimpleViewManager<ReactWebView>(), CustomWebViewManagerInterface<ReactWebView> {
private val delegate: CustomWebViewManagerDelegate<ReactWebView, ReactWebViewManager> =
CustomWebViewManagerDelegate(this)
override fun getDelegate(): ViewManagerDelegate<ReactWebView> = delegate
override fun getName(): String = REACT_CLASS
override fun createViewInstance(context: ThemedReactContext): ReactWebView = ReactWebView(context)
@ReactProp(name = "sourceUrl")
override fun setSourceURL(view: ReactWebView, sourceURL: String?) {
if (sourceURL == null) {
view.emitOnScriptLoaded(ReactWebView.OnScriptLoadedEventResult.error)
return;
}
view.loadUrl(sourceURL, emptyMap())
}
companion object {
const val REACT_CLASS = "CustomWebView"
}
override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any> =
mapOf(
"onScriptLoaded" to
mapOf(
"phasedRegistrationNames" to
mapOf(
"bubbled" to "onScriptLoaded",
"captured" to "onScriptLoadedCapture"
)))
}
ReactWebViewManager extiende la clase SimpleViewManager de React e implementa CustomWebViewManagerInterface, generado por Codegen.
Contiene una referencia al CustomWebViewManagerDelegate, otro elemento generado por Codegen.
Luego sobrescribe la función getName, que debe devolver el mismo nombre usado en la llamada a codegenNativeComponent de la especificación.
La función createViewInstance es responsable de instanciar un nuevo ReactWebView.
Luego, el ViewManager debe definir cómo las props de los componentes de React actualizarán la vista nativa. En este ejemplo, debes decidir cómo manejar la propiedad sourceURL que React establecerá en el WebView.
Finalmente, si el componente puede emitir eventos, debes mapear el nombre del evento sobrescribiendo getExportedCustomBubblingEventTypeConstants para eventos burbujeantes, o getExportedCustomDirectEventTypeConstants para eventos directos.
4. Escribe el ReactWebViewPackage
Al igual que con los Módulos Nativos, los Componentes Nativos también necesitan implementar la clase ReactPackage. Este objeto se utiliza para registrar el componente en el entorno de ejecución de React Native.
Este es el código para el ReactWebViewPackage:
- Java
- Kotlin
package com.webview;
import com.facebook.react.BaseReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.uimanager.ViewManager;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ReactWebViewPackage extends BaseReactPackage {
@Override
public List<ViewManager<?, ?>> createViewManagers(ReactApplicationContext reactContext) {
return Collections.singletonList(new ReactWebViewManager(reactContext));
}
@Override
public NativeModule getModule(String s, ReactApplicationContext reactApplicationContext) {
if (ReactWebViewManager.REACT_CLASS.equals(s)) {
return new ReactWebViewManager(reactApplicationContext);
}
return null;
}
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return new ReactModuleInfoProvider() {
@Override
public Map<String, ReactModuleInfo> getReactModuleInfos() {
Map<String, ReactModuleInfo> map = new HashMap<>();
map.put(ReactWebViewManager.REACT_CLASS, new ReactModuleInfo(
ReactWebViewManager.REACT_CLASS, // name
ReactWebViewManager.REACT_CLASS, // className
false, // canOverrideExistingModule
false, // needsEagerInit
false, // isCxxModule
true // isTurboModule
));
return map;
}
};
}
}
package com.webview
import com.facebook.react.BaseReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider
import com.facebook.react.uimanager.ViewManager
class ReactWebViewPackage : BaseReactPackage() {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return listOf(ReactWebViewManager(reactContext))
}
override fun getModule(s: String, reactApplicationContext: ReactApplicationContext): NativeModule? {
when (s) {
ReactWebViewManager.REACT_CLASS -> ReactWebViewManager(reactApplicationContext)
}
return null
}
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
mapOf(ReactWebViewManager.REACT_CLASS to ReactModuleInfo(
name = ReactWebViewManager.REACT_CLASS,
className = ReactWebViewManager.REACT_CLASS,
canOverrideExistingModule = false,
needsEagerInit = false,
isCxxModule = false,
isTurboModule = true,
)
)
}
}
El ReactWebViewPackage extiende BaseReactPackage e implementa todos los métodos requeridos para registrar correctamente nuestro componente.
-
El método
createViewManagerses la fábrica que crea losViewManagerque gestionan las vistas personalizadas. -
El método
getModuledevuelve el ViewManager adecuado según la vista que React Native necesite renderizar. -
El
getReactModuleInfoProviderproporciona toda la información requerida al registrar el módulo en el entorno de ejecución.
5. Registra el ReactWebViewPackage en la aplicación
Finalmente, debes registrar el ReactWebViewPackage en la aplicación. Esto se hace modificando el archivo MainApplication, añadiendo el ReactWebViewPackage a la lista de paquetes devueltos por la función getPackages.
package com.demo
import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader
import com.webview.ReactWebViewPackage
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(ReactWebViewPackage())
}
override fun getJSMainModuleName(): String = "index"
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}
override val reactHost: ReactHost
get() = getDefaultReactHost(applicationContext, reactNativeHost)
override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
load()
}
}
}
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Ahora es momento de escribir código para la plataforma iOS para poder renderizar la vista web. Los pasos que debes seguir son:
-
Ejecutar Codegen.
-
Escribir el código para el
RCTWebView -
Registrar el
RCTWebViewen la aplicación
1. Ejecutar Codegen
Puedes ejecutar manualmente Codegen, aunque es más sencillo usar la aplicación donde probarás el componente para que lo haga por ti.
cd ios
bundle install
bundle exec pod install
Es importante que verás la salida de registro de Codegen, que usaremos en Xcode para construir nuestro componente nativo WebView.
Debes tener cuidado al confirmar código generado en tu repositorio. El código generado es específico para cada versión de React Native. Usa peerDependencies de npm para restringir la compatibilidad con la versión de React Native.
3. Escribir el RCTWebView
Necesitamos preparar tu proyecto de iOS usando Xcode completando estos 5 pasos:
- Abre el espacio de trabajo de Xcode generado por CocoaPods:
cd ios
open Demo.xcworkspace
- Haz clic derecho en la aplicación y selecciona
New Group, llama al nuevo grupoWebView.
- En el grupo
WebView, creaNew→File from Template.
- Usa la plantilla
Objective-C Filey asígnale el nombreRCTWebView.
-
Repite el paso 4 y crea un archivo de encabezado llamado
RCTWebView.h. -
Renombra
RCTWebView.m→RCTWebView.mmpara convertirlo en un archivo Objective-C++.
Podfile
...
Demo
├── AppDelegate.swift
...
├── RCTWebView.h
└── RCTWebView.mm
Después de crear el archivo de encabezado y el archivo de implementación, puedes comenzar a implementarlos.
Este es el código para el archivo RCTWebView.h, que declara la interfaz del componente.
#import <React/RCTViewComponentView.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTWebView : RCTViewComponentView
// You would declare native methods you'd want to access from the view here
@end
NS_ASSUME_NONNULL_END
Esta clase define un RCTWebView que extiende la clase RCTViewComponentView. Esta es la clase base para todos los componentes nativos y es proporcionada por React Native.
El código para el archivo de implementación (RCTWebView.mm) es el siguiente:
#import "RCTWebView.h"
#import <react/renderer/components/AppSpec/ComponentDescriptors.h>
#import <react/renderer/components/AppSpec/EventEmitters.h>
#import <react/renderer/components/AppSpec/Props.h>
#import <react/renderer/components/AppSpec/RCTComponentViewHelpers.h>
#import <WebKit/WebKit.h>
using namespace facebook::react;
@interface RCTWebView () <RCTCustomWebViewViewProtocol, WKNavigationDelegate>
@end
@implementation RCTWebView {
NSURL * _sourceURL;
WKWebView * _webView;
}
-(instancetype)init
{
if(self = [super init]) {
_webView = [WKWebView new];
_webView.navigationDelegate = self;
[self addSubview:_webView];
}
return self;
}
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast<CustomWebViewProps const>(_props);
const auto &newViewProps = *std::static_pointer_cast<CustomWebViewProps const>(props);
// Handle your props here
if (oldViewProps.sourceURL != newViewProps.sourceURL) {
NSString *urlString = [NSString stringWithCString:newViewProps.sourceURL.c_str() encoding:NSUTF8StringEncoding];
_sourceURL = [NSURL URLWithString:urlString];
if ([self urlIsValid:newViewProps.sourceURL]) {
[_webView loadRequest:[NSURLRequest requestWithURL:_sourceURL]];
}
}
[super updateProps:props oldProps:oldProps];
}
-(void)layoutSubviews
{
[super layoutSubviews];
_webView.frame = self.bounds;
}
#pragma mark - WKNavigationDelegate
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Success};
self.eventEmitter.onScriptLoaded(result);
}
- (BOOL)urlIsValid:(std::string)propString
{
if (propString.length() > 0 && !_sourceURL) {
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Error};
self.eventEmitter.onScriptLoaded(result);
return NO;
}
return YES;
}
// Event emitter convenience method
- (const CustomWebViewEventEmitter &)eventEmitter
{
return static_cast<const CustomWebViewEventEmitter &>(*_eventEmitter);
}
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<CustomWebViewComponentDescriptor>();
}
@end
Este código está escrito en Objective-C++ y contiene varios detalles:
-
la
@interfaceimplementa dos protocolos:RCTCustomWebViewViewProtocol, generado por Codegen;WKNavigationDelegate, proporcionado por el framework WebKit para manejar eventos de navegación de la vista web;
-
el método
initque instancia elWKWebView, lo añade a las subvistas y establece elnavigationDelegate; -
el método
updatePropsque es llamado por React Native cuando las props del componente cambian; -
el método
layoutSubviewsque describe cómo se debe diseñar la vista personalizada; -
el método
webView:didFinishNavigation:que te permite manejar qué hacer cuando elWKWebViewtermina de cargar la página; -
el método
urlIsValid:(std::string)propStringque verifica si la URL recibida como prop es válida; -
el método
eventEmitterque es una utilidad para recuperar una instancia fuertemente tipada deeventEmitter -
el
componentDescriptorProviderque devuelve elComponentDescriptorgenerado por Codegen;
Agregar el framework WebKit
Este paso solo es necesario porque estamos creando una vista web. Los componentes web en iOS deben vincularse con el framework WebKit proporcionado por Apple. Si tu componente no necesita acceder a funciones específicas de la web, puedes omitir este paso.
Una vista web requiere acceso a ciertas funcionalidades que Apple proporciona a través de uno de los frameworks incluidos con Xcode y los dispositivos: WebKit.
Puedes verlo en el código nativo por la línea #import <WebKit/WebKit.h> agregada en RCTWebView.mm.
Para vincular el framework WebKit en tu aplicación, sigue estos pasos:
-
En Xcode, haz clic en tu proyecto
-
Selecciona el destino de la aplicación
-
Selecciona la pestaña General
-
Desplázate hacia abajo hasta encontrar la sección "Frameworks, Libraries, and Embedded Contents" y presiona el botón
+
-
En la barra de búsqueda, filtra por WebKit
-
Selecciona el framework WebKit
-
Haz clic en Agregar.

3. Usa tu componente nativo
Finalmente, puedes usar el nuevo componente en tu aplicación. Actualiza tu archivo generado App.tsx con:
import React from 'react';
import {Alert, StyleSheet, View} from 'react-native';
import WebView from './specs/WebViewNativeComponent';
function App(): React.JSX.Element {
return (
<View style={styles.container}>
<WebView
sourceURL="https://react.dev/"
style={styles.webview}
onScriptLoaded={() => {
Alert.alert('Page Loaded');
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
alignContent: 'center',
},
webview: {
width: '100%',
height: '100%',
},
});
export default App;
Este código crea una aplicación que utiliza el nuevo componente WebView que creamos para cargar el sitio web react.dev.
La aplicación también muestra una alerta cuando la página web se carga.
4. Ejecuta tu aplicación usando el componente WebView
- Android
- iOS
yarn run android
yarn run ios
| Android | iOS |
|---|---|
![]() | ![]() |

