0

I've been having this problem for a really long time now. I'm really hoping someone has overcome this issue.

We already have an app that is being developed, but we have decided to build micro-frontends to strip out some features into separate modules. The existing app does not have this problem.

I've created a react native module with pure react-native, with an example app within it to test it which uses expo. The code is mostly copied over from the existing app mentioned above, with some changes here and there. The module uses react-native-paper.

The example app runs fine on both ios and android, until I add a component from the module that makes use of an icon.

For example, from my module, this code exists:

 <Modal visible={visible} onDismiss={() => setVisible(false)}>
  <Surface style={styles.containerStyle}>
    <Appbar.Header>
      <Appbar.Action
        icon={{
          uri: 'https://avatars0.githubusercontent.com/u/17571969?v=3&s=400',
        }}
        testID={`${props.textInputProps?.testID}Close`}
        onPress={() => setVisible(false)}
        touchSoundDisabled={false}
      />

I've also tried:

<Modal visible={visible} onDismiss={() => setVisible(false)}>
  <Surface style={styles.containerStyle}>
    <Appbar.Header>
      <Appbar.Action
        icon={'close'}
        testID={`${props.textInputProps?.testID}Close`}
        onPress={() => setVisible(false)}
        touchSoundDisabled={false}
      />

and:

 <Modal visible={visible} onDismiss={() => setVisible(false)}>
  <Surface style={styles.containerStyle}>
    <Appbar.Header>
      <Appbar.Action
        icon={() => <MaterialCommunityIcons name="close" />}

(This one gives a different error): Unable to resolve module @expo/vector-icons/MaterialCommunityIcons from /../../../../../src/models/FormModel.tsx: @expo/vector-icons/MaterialCommunityIcons could not be found within the project or in these directories: ../node_modlues or ../../../../node_modules

This is from my example code, where the module code with the icon is being called, with comments specifying the outcome:

 <FormBuilder
          control={formControl.control}
          setFocus={formControl.setFocus}
          formConfigArray={[
            formModel.AddressLine1,
            formModel.AddressLine2,
            formModel.City,
            formModel.Country, //component with icon that gives error
          ]}
        />
        <Button icon="car">Press me</Button> {/* works perfectly */}
        <IconButton icon="check" /> {/* works perfectly */}

Once I add an icon from react-native-paper, from any component, I start getting this error:

Render Error - undefined is not an object (evaluating '_$$_REQUIRE(_dependencyMap[6], "react/jsx-runtime").jsx') within MaterialCommunitIcons within react-native-paper

Uncaught Error - Requiring unknown module "undefined". If you are sure the module exists, try restarting Metro. You may also want to run 'yarn' or 'npm install'

I get the same error on Android.

I've tried loading the fonts within the component, but I just cannot seem to over come this issue.

To note, if I add an icon from my example folder directly, I do not get any errors and the icon renders

This is the error I get in the terminal:

Error: Requiring unknown module "undefined". If you are sure the module exists, try restarting Metro. You may also want to run `yarn` or `npm install`.
Error: Requiring unknown module "undefined". If you are sure the module exists, try restarting Metro. You may also want to run `yarn` or `npm install`.
TypeError: undefined is not an object (evaluating '_$$_REQUIRE(_dependencyMap[6], "react/jsx-runtime").jsx')

This error is located at:
    in Icon
    in ThemedComponent (created by withTheme(Icon))
    in withTheme(Icon) (created by IconButton)
    in RCTView (created by View)
    in View (created by IconButton)
    in RCTView (created by View)
    in View (created by TouchableHighlight)
    in TouchableHighlight
    in Unknown (created by TouchableRipple)
    in TouchableRipple
    in ThemedComponent (created by withTheme(TouchableRipple))
    in withTheme(TouchableRipple) (created by IconButton)
    in IconButton
    in ThemedComponent (created by withTheme(IconButton))
    in withTheme(IconButton) (created by TextInput.Icon)
    in RCTView (created by View)
    in View (created by TextInput.Icon)
    in TextInput.Icon (created by Logic)
    in IconAdornment (created by TextInputAdornment)
    in TextInputAdornment (created by TextInputOutlined)
    in RCTView (created by View)
    in View (created by TextInputOutlined)
    in RCTView (created by View)
    in View (created by TextInputOutlined)
    in TextInputOutlined (created by TextInput)
    in TextInput
    in ThemedComponent (created by withTheme(TextInput))
    in withTheme(TextInput) (created by InputAutocomplete)
    in RCTView (created by View)
    in View (created by InputAutocomplete)
    in RCTView (created by View)
    in View (created by TouchableHighlight)
    in TouchableHighlight
    in Unknown (created by TouchableRipple)
    in TouchableRipple
    in ThemedComponent (created by withTheme(TouchableRipple))
    in withTheme(TouchableRipple) (created by InputAutocomplete)
    in InputAutocomplete (created by Logic)
    in Logic (created by FormBuilder)
    in FormBuilder (created by App)
    in RCTScrollContentView (created by ScrollView)
    in RCTScrollView (created by ScrollView)
    in ScrollView (created by ScrollView)
    in ScrollView (created by App)
    in RCTView (created by View)
    in View (created by App)
    in App (created by ExpoRoot)
    in ExpoRoot
    in RCTView (created by View)
    in View (created by AppContainer)
    in RCTView (created by View)
    in View (created by AppContainer)
    in AppContainer
TypeError: undefined is not an object (evaluating '_$$_REQUIRE(_dependencyMap[6], "react/jsx-runtime").jsx')

This error is located at:
    in Icon
    in ThemedComponent (created by withTheme(Icon))
    in withTheme(Icon) (created by IconButton)
    in RCTView (created by View)
    in View (created by IconButton)
    in RCTView (created by View)
    in View (created by TouchableHighlight)
    in TouchableHighlight
    in Unknown (created by TouchableRipple)
    in TouchableRipple
    in ThemedComponent (created by withTheme(TouchableRipple))
    in withTheme(TouchableRipple) (created by IconButton)
    in IconButton
    in ThemedComponent (created by withTheme(IconButton))
    in withTheme(IconButton) (created by TextInput.Icon)
    in RCTView (created by View)
    in View (created by TextInput.Icon)
    in TextInput.Icon (created by Logic)
    in IconAdornment (created by TextInputAdornment)
    in TextInputAdornment (created by TextInputOutlined)
    in RCTView (created by View)
    in View (created by TextInputOutlined)
    in RCTView (created by View)
    in View (created by TextInputOutlined)
    in TextInputOutlined (created by TextInput)
    in TextInput
    in ThemedComponent (created by withTheme(TextInput))
    in withTheme(TextInput) (created by InputAutocomplete)
    in RCTView (created by View)
    in View (created by InputAutocomplete)
    in RCTView (created by View)
    in View (created by TouchableHighlight)
    in TouchableHighlight
    in Unknown (created by TouchableRipple)
    in TouchableRipple
    in ThemedComponent (created by withTheme(TouchableRipple))
    in withTheme(TouchableRipple) (created by InputAutocomplete)
    in InputAutocomplete (created by Logic)
    in Logic (created by FormBuilder)
    in FormBuilder (created by App)
    in RCTScrollContentView (created by ScrollView)
    in RCTScrollView (created by ScrollView)
    in ScrollView (created by ScrollView)
    in ScrollView (created by App)
    in RCTView (created by View)
    in View (created by App)
    in App (created by ExpoRoot)
    in ExpoRoot
    in RCTView (created by View)
    in View (created by AppContainer)
    in RCTView (created by View)
    in View (created by AppContainer)
    in AppContainer

This is my package.json:

{
  "main": "index.js",
  "scripts": {
    "start": "expo start --dev-client",
    "android": "expo start --android",
    "ios": "expo run:ios",
    "web": "expo start --web",
    "test": "jest",
    "e2e:test": "detox test -c android.emu.release  --debug-synchronization 3000 --take-screenshots failing",
    "e2e:build": "RN_SRC_EXT=e2e.ts detox build -c android.emu.release",
    "e2e:ci": "npm run e2e:build && npm run e2e:test",
    "e2e:test-ios": "detox test -c ios.emu.release --debug-synchronization 3000 --take-screenshots failing",
    "e2e:test-ios-showHierarchy": "detox test -c ios.emu.release --debug-synchronization 3000 --take-screenshots failing --capture-view-hierarchy enabled",
    "e2e:build-ios": "RN_SRC_EXT=e2e.ts detox build -c ios.emu.release",
    "e2e:ci-ios": "npm run e2e:build-ios && npm run e2e:test-ios"
  },
  "dependencies": {
    "expo": "~44.0.2",
    "expo-app-loading": "~1.3.0",
    "expo-splash-screen": "~0.14.1",
    "expo-status-bar": "~1.2.0",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-native": "0.64.3",
    "react-native-web": "0.17.1"
  },
  "devDependencies": {
    "@babel/core": "^7.16.5",
    "@babel/preset-env": "^7.16.5",
    "@babel/preset-typescript": "^7.16.5",
    "@expo/vector-icons": "^12.0.5",
    "@types/jest": "^27.0.3",
    "@types/react": "^17.0.38",
    "@types/react-native": "^0.66.10",
    "@types/react-test-renderer": "^17.0.1",
    "babel-jest": "^27.4.5",
    "babel-plugin-module-resolver": "^4.1.0",
    "detox": "^19.3.1",
    "jest": "^27.4.5",
    "react-native-vector-icons": "^9.0.0",
    "typescript": "^4.5.4"
  },
  "private": true
}

my app.json:

{
  "expo": {
    "name": "example",
    "slug": "example",
    "version": "1.0.0",
    "icon": "./appIcon.png",
    "assetBundlePatterns": [
      "**/*"
    ]
  },
  "name": "example"
}

my babel.config.ts:

const path = require("path");
const pak = require("../package.json");

module.exports = function (api) {
  api.cache(true);
  return {
    presets: [
      "babel-preset-expo",
      ["@babel/preset-env", { targets: { node: "current" } }],
      "@babel/preset-typescript",
    ],
    plugins: [
    [
      "module-resolver",
      {
        extensions: [".tsx", ".ts", ".js", ".json"],
        alias: {
          [pak.name]: path.join(__dirname, "..", pak.source),
        }
      }
    ]
  ]
  };
};

my metro.config.js:

const path = require("path");
const blacklist = require("metro-config/src/defaults/exclusionList");
const escape = require("escape-string-regexp");
const pak = require("../package.json");

const root = path.resolve(__dirname, "..");

const modules = Object.keys({
  ...pak.peerDependencies,
});

module.exports = {
  projectRoot: __dirname,
  watchFolders: [root],

  // We need to make sure that only one version is loaded for peerDependencies
  // So we blacklist them at the root, and alias them to the versions in example's node_modules
  resolver: {
    blacklistRE: blacklist(
      modules.map(
        (m) =>
          new RegExp(`^${escape(path.join(root, "node_modules", m))}\\/.*$`)
      )
    ),

    extraNodeModules: modules.reduce((acc, name) => {
      acc[name] = path.join(__dirname, "node_modules", name);
      return acc;
    }, {}),
  },

  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
};

Please help!!

1 Answers1

0

my colleague managed to figure this out, so I thought to share it here in case anyone else comes across this issue.

What's happening is that the example app is getting confused regarding which vector icons it should use, as expo within the example app as vector icons and the module outside the example app has react native vector icons. The key is to specify in your module, that these dependencies should come from the calling app and not the module.

To do that, you specify these modules in your "peerDependencies" in the module, so that the npm package from example is used, instead of the npm package from the module:

  "peerDependencies": {
     "@expo/vector-icons": "*",
     "expo-font": "*",
     "lodash": "*",
     "lodash.isstring": "*",
     "react": "*",
     "react-native": "*",
     "react-native-vector-icons": "*",
     "react-native-paper": "*"
   },

Hope this helps someone else in the world out there, as it has me.

PS, I owe my colleague a months worth of coffee now!