1import React from 'react'; 2import { findNodeHandle, NativeModules, requireNativeComponent } from 'react-native'; 3import { requireNativeModule } from './requireNativeModule'; 4/** 5 * A map that caches registered native components. 6 */ 7const nativeComponentsCache = new Map(); 8/** 9 * Requires a React Native component from cache if possible. This prevents 10 * "Tried to register two views with the same name" errors on fast refresh, but 11 * also when there are multiple versions of the same package with native component. 12 */ 13function requireCachedNativeComponent(viewName) { 14 const cachedNativeComponent = nativeComponentsCache.get(viewName); 15 if (!cachedNativeComponent) { 16 const nativeComponent = requireNativeComponent(viewName); 17 nativeComponentsCache.set(viewName, nativeComponent); 18 return nativeComponent; 19 } 20 return cachedNativeComponent; 21} 22/** 23 * A drop-in replacement for `requireNativeComponent`. 24 */ 25export function requireNativeViewManager(viewName) { 26 const { viewManagersMetadata } = NativeModules.NativeUnimoduleProxy; 27 const viewManagerConfig = viewManagersMetadata?.[viewName]; 28 if (__DEV__ && !viewManagerConfig) { 29 const exportedViewManagerNames = Object.keys(viewManagersMetadata).join(', '); 30 console.warn(`The native view manager required by name (${viewName}) from NativeViewManagerAdapter isn't exported by expo-modules-core. Views of this type may not render correctly. Exported view managers: [${exportedViewManagerNames}].`); 31 } 32 // Set up the React Native native component, which is an adapter to the universal module's view 33 // manager 34 const reactNativeViewName = `ViewManagerAdapter_${viewName}`; 35 const ReactNativeComponent = requireCachedNativeComponent(reactNativeViewName); 36 const proxiedPropsNames = viewManagerConfig?.propsNames ?? []; 37 class NativeComponent extends React.PureComponent { 38 static displayName = viewName; 39 // This will be accessed from native when the prototype functions are called, 40 // in order to find the associated native view. 41 nativeTag = null; 42 componentDidMount() { 43 this.nativeTag = findNodeHandle(this); 44 } 45 render() { 46 const nativeProps = omit(this.props, proxiedPropsNames); 47 const proxiedProps = pick(this.props, proxiedPropsNames); 48 return React.createElement(ReactNativeComponent, { ...nativeProps, proxiedProperties: proxiedProps }); 49 } 50 } 51 try { 52 const nativeModule = requireNativeModule(viewName); 53 const nativeViewPrototype = nativeModule.ViewPrototype; 54 if (nativeViewPrototype) { 55 // Temporarily each function is wrapped to pass the native tag as the argument. 56 // In the future, native will automatically get the tag from `this` and then we can simply use 57 // `Object.assign(NativeComponent.prototype, nativeViewPrototype)` instead. 58 for (const key of Object.getOwnPropertyNames(nativeViewPrototype)) { 59 NativeComponent.prototype[key] = function (...args) { 60 return nativeViewPrototype[key].call(this, this.nativeTag, ...args); 61 }; 62 } 63 } 64 } 65 catch { 66 // `requireNativeModule` may throw an error when the native module cannot be found. 67 // In some tests we don't mock the entire modules, but we do want to mock native views. For now, 68 // until we still have to support the legacy modules proxy and don't have better ways to mock, 69 // let's just gracefully skip assigning the prototype functions. 70 // See: https://github.com/expo/expo/blob/main/packages/expo-modules-core/src/__tests__/NativeViewManagerAdapter-test.native.tsx 71 } 72 return NativeComponent; 73} 74function omit(props, propNames) { 75 const copied = { ...props }; 76 for (const propName of propNames) { 77 delete copied[propName]; 78 } 79 return copied; 80} 81function pick(props, propNames) { 82 return propNames.reduce((prev, curr) => { 83 if (curr in props) { 84 prev[curr] = props[curr]; 85 } 86 return prev; 87 }, {}); 88} 89//# sourceMappingURL=NativeViewManagerAdapter.native.js.map