1import React from 'react'; 2import { NativeModules, UIManager, ViewPropTypes, requireNativeComponent } from 'react-native'; 3 4// To make the transition from React Native's `requireNativeComponent` to Expo's 5// `requireNativeViewManager` as easy as possible, `requireNativeViewManager` is a drop-in 6// replacement for `requireNativeComponent`. 7// 8// For each view manager, we create a wrapper component that accepts all of the props available to 9// the author of the universal module. This wrapper component splits the props into two sets: props 10// passed to React Native's View (ex: style, testID) and custom view props, which are passed to the 11// adapter view component in a prop called `proxiedProperties`. 12 13// NOTE: React Native is moving away from runtime PropTypes and may remove ViewPropTypes, in which 14// case we will need another way to separate standard React Native view props from other props, 15// which we proxy through the adapter 16const ViewPropTypesKeys = Object.keys(ViewPropTypes); 17 18type NativeExpoComponentProps = { 19 proxiedProperties: object; 20}; 21 22/** 23 * A drop-in replacement for `requireNativeComponent`. 24 */ 25export function requireNativeViewManager<P = any>(viewName: string): React.ComponentType<P> { 26 if (__DEV__) { 27 const { NativeUnimoduleProxy } = NativeModules; 28 if (!NativeUnimoduleProxy.viewManagersNames.includes(viewName)) { 29 const exportedViewManagerNames = NativeUnimoduleProxy.viewManagersNames.join(', '); 30 console.warn( 31 `The native view manager required by name (${viewName}) from NativeViewManagerAdapter isn't exported by @unimodules/react-native-adapter. Views of this type may not render correctly. Exported view managers: [${exportedViewManagerNames}].` 32 ); 33 } 34 } 35 36 // Set up the React Native native component, which is an adapter to the universal module's view 37 // manager 38 const reactNativeViewName = `ViewManagerAdapter_${viewName}`; 39 const ReactNativeComponent = requireNativeComponent<NativeExpoComponentProps>( 40 reactNativeViewName 41 ); 42 const reactNativeUIConfiguration = (UIManager.getViewManagerConfig 43 ? UIManager.getViewManagerConfig(reactNativeViewName) 44 : UIManager[reactNativeViewName]) || { 45 NativeProps: {}, 46 directEventTypes: {}, 47 }; 48 const reactNativeComponentPropNames = [ 49 'children', 50 ...ViewPropTypesKeys, 51 ...Object.keys(reactNativeUIConfiguration.NativeProps), 52 ...Object.keys(reactNativeUIConfiguration.directEventTypes), 53 ]; 54 55 // Define a component for universal-module authors to access their native view manager 56 function NativeComponentAdapter(props, ref) { 57 const nativeProps = pick(props, reactNativeComponentPropNames); 58 const proxiedProps = omit(props, reactNativeComponentPropNames); 59 return <ReactNativeComponent {...nativeProps} proxiedProperties={proxiedProps} ref={ref} />; 60 } 61 NativeComponentAdapter.displayName = `Adapter<${viewName}>`; 62 return React.forwardRef(NativeComponentAdapter); 63} 64 65function omit(props: Record<string, any>, propNames: string[]) { 66 const copied = { ...props }; 67 for (const propName of propNames) { 68 delete copied[propName]; 69 } 70 return copied; 71} 72 73function pick(props: Record<string, any>, propNames: string[]) { 74 return propNames.reduce((prev, curr) => { 75 if (curr in props) { 76 prev[curr] = props[curr]; 77 } 78 return prev; 79 }, {}); 80} 81