React Native App Stuck On Jetify in dev mode - react-native

I'm trying to migrate my app on 0.60, but it keeps getting stuck on jetify process.
Basically, after this line:
info Running jetifier to migrate libraries to AndroidX. You can disable it using "--no-jetifier" flag.
It's just stuck and does nothing. And I need jetifier for react-native-firebase and some other native libraries. Any suggestions?
Update
It seems the problem is in symlinks. I'm using Lerna.

There is also one more situation when the npx react-native run-android stucks at jetifying state
If you run npm run android it'll start the metro server as well,
However, to re-run npm run android, you need to close the metro command window and allow npm run android to start a new metro server

In the end what I did was rewriting a jetify specifying which modules I need to jetify.
My jetify index.js:
const {fork} = require('child_process');
const {join} = require('path');
const {getClassesMapping, readDir, chunk} = require('./src/utils');
const cpus = require('os').cpus().length;
const arg = process.argv.slice(2)[0];
const mode = arg && (arg === 'reverse' || arg === '-r') ? 'reverse' : 'forward';
const SEARCH_DIRS = [
........modules here
];
const classesMapping = getClassesMapping();
for (const SEARCH_DIR of SEARCH_DIRS) {
const files = readDir(SEARCH_DIR);
console.log(
`Jetifier found ${
files.length
} file(s) to ${mode}-jetify. Using ${cpus} workers...`,
);
for (const filesChunk of chunk(files, cpus)) {
const worker = fork(join(__dirname, 'src', 'worker.js'));
worker.send({filesChunk, classesMapping, mode});
}
}
And postinstall in package.json:
"postinstall": "cp ./jetify.js ./node_modules/jetifier/index.js"

Related

React-Native Expo Export Web Receiving Error 'Invalid Project Root' While Building Production App

Hi I am receiving the error that the project root is invalid. I will also add that I am using expo alongside my project.
This happens when executing the command npx expo export:web
Also happens when executing the command npx expo build
webpack.config.js
module.exports = function (api) {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: ["react-native-reanimated/plugin"],
};
};
metro.config.js
(Not sure if metro is relevant as I believe it is more for development purposes...)
const { getDefaultConfig } = require("#expo/metro-config");
const defaultConfig = getDefaultConfig(__dirname);
defaultConfig.resolver.assetExts.push("cjs");
module.exports = defaultConfig;
react-native.config.js
module.exports = {
project: {
ios: {},
android: {}, // grouped into "project"
web: {},
},
assets: ["./assets/fonts"], // stays the same
};
npm start works fine and everything works accordingly in the browser. The goal is to build this for production and begin hosting on a web server.
I am hoping that I am simply missing a location to a directory in a config file but any insight is appreciated.
First, just run it with expo start, after it started press w.

_reactNative.Keyboard.removeListener is not a function

I just upgraded from RN 0.63.2 to 0.64.2 then to 0.65.0-rc.3. Now I started to get this exception while trying to navigate between screens: _reactNative.Keyboard.removeListener is not a function.
The problem seems to be in BottomTabBar.js. The code snippet is below.
The React-navigation version is 4.4.0. React-navigation-tabs version is 1.2.0.
I checked the Keyboard class' methods and actually, I CAN see a method called "removeListener". Even the auto-complete shows it.
Also, I can see in the react-native documentation that this method exists:
So, I don't see why I am getting this exception. Any help is much appreciated.
Updating the react-navigation version could be a solution, not sure about that, but I would like to avoid that path if possible since it has been working perfectly till now.
Thanks.
Edit: Just realized this exception is not thrown when navigating to a screen for the first time. It is thrown when I revisit a screen that has been previously visited.
Edit2: I am seeing this behavior on android. Haven't been able to compile the project on iOS yet.
Edit3: npm list react-native-tab-view command shows different versions for react-native-tab-view
├── react-native-tab-view#2.15.1
├─┬ react-navigation-drawer#1.4.0
│ └── react-native-tab-view#1.4.1
└─┬ react-navigation-tabs#1.2.0
└── react-native-tab-view#1.4.1
I did npm install react-native-tab-view#1.4.1 but nothing changed. I mean the output of the above npm list command changed and it all showed version 1.4.1 for react-native-tab-view but the behavior did not change.
removeListener method has been deprecated. It is suggested to call remove on the subscriptionEvent rather than calling removeListener.
Replace those lines as follows and use patch-package to commit your changes -
componentDidMount() {
if (Platform.OS === 'ios') {
this.keyboardWillShow = Keyboard.addListener('keyboardWillShow', this._handleKeyboardShow);
this.keyboardWillHide = Keyboard.addListener('keyboardWillHide', this._handleKeyboardHide);
} else {
this.keyboardDidShow = Keyboard.addListener('keyboardDidShow', this._handleKeyboardShow);
this.keyboardDidHide = Keyboard.addListener('keyboardDidHide', this._handleKeyboardHide);
}
}
componentWillUnmount() {
if (Platform.OS === 'ios') {
this.keyboardWillShow?.remove();
this.keyboardWillHide?.remove();
} else {
this.keyboardDidShow?.remove();
this.keyboardDidHide?.remove();
}
}
This fixed my issue:
modify node_modules/react-native-gifted-chat/lib/MessageContainer.js
this.attachKeyboardListeners = () => {
const { invertibleScrollViewProps: invertibleProps } = this.props;
if (invertibleProps) {
- Keyboard.addListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
- Keyboard.addListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
- Keyboard.addListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
- Keyboard.addListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
+ this.willShowSub = Keyboard.addListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
+ this.didShowSub = Keyboard.addListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
+ this.willHideSub = Keyboard.addListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
+ this.didHideSub = Keyboard.addListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
}
};
this.detachKeyboardListeners = () => {
const { invertibleScrollViewProps: invertibleProps } = this.props;
- Keyboard.removeListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
- Keyboard.removeListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
- Keyboard.removeListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
- Keyboard.removeListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
+ this.willShowSub?.remove();
+ this.didShowSub?.remove();
+ this.willHideSub?.remove();
+ this.didHideSub?.remove();
};
then, run:
# run patch-package to create a .patch file
npx patch-package react-native-gifted-chat
# commit the patch file to share the fix with your team
git add patches/some-package+0.16.3.patch
git commit -m "fix MessageContainer.js in react-native-gifted-chat"
Based on this response:
https://github.com/FaridSafi/react-native-gifted-chat/issues/2090#issuecomment-901812607
I solved this modifying the file in node_modules/react-native/Libraries/Components/Keyboard/Keyboard.js and persisting the change using the patch-package library
You need rename the removeEventListener function to removeListener
Then you run in the project folder:
yarn patch-package react-native
This will create a patches folder in the root and a file with the changes. That's it.
I had similar issue while I used "#react-navigation/material-bottom-tabs": "^6.2.4". I found that issue file was in react-native-paper#3.12.0 package. I have just updated it to react-native-paper#4.12.5 and it solved the issue.

How to configure with create-react-native-app metro bundler

I'm new to react-native, this community is awesome.
but i'm having an issue with configuring https://github.com/terrylinla/react-native-sketch-canvas with npx create-react-native-app .
all I've done so far is edit the metro.config.js
const extraNodeModules = {
'#terrylinla/react-native-sketch-canvas': './node_modules/#terrylinla\react-native-sketch-canvas/'
}
const resolverMainFields = ['browser','main'];
module.exports = {
resolver: {
extraNodeModules,
resolverMainFields
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
then cmd react-native run-android it bundles correctly but it never loads onto the emulator:
this is what it says in shell:
info Running jetifier to migrate libraries to AndroidX. You can disable it using "--no-jetifier" flag.
(node:8712) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use node --trace-warnings ... to show where the warning was created)
Jetifier found 987 file(s) to forward-jetify. Using 4 workers...
info Starting JS server...
info Launching emulator...
info Successfully launched emulator.
info Installing the app...
Configure project :terrylinla_react-native-sketch-canvas
WARNING: Configuration 'provided' is obsolete and has been replaced with 'compileOnly'.
It will be removed soon. For more information see: http://d.android.com/r/tools/update-dependency-configurations.html
Task :app:stripDebugDebugSymbols UP-TO-DATE
Compatible side by side NDK version was not found.
<============-> 99% EXECUTING [10m 30s]
IDLE
IDLE
:app:installDebug
IDLE
So I just got metro to bundle my js files correctly. I discovered it was something to do with the react-native android gradle it's very confusing. But I just restarted my computer and ran the same commands. That fixed it.
these stackoverflow answers worked before for me to get my build working:
Stuck on info starting JS server
React-native run-android stuck at 99% appDebug
I hope this helps to anyone using create-react-native-app & metro-bundler

Detect development environment in react-native.config.js

I want to access __DEV__ in react-native.config.js so I can manage some native libraries linking in the development & production environments like sample bellow:
const VECTOR_ICONS_FONTS_PATH =
'./node_modules/react-native-vector-icons/Fonts';
const VECTOR_FONTS = ['MaterialCommunityIcons.ttf'];
module.exports = {
assets: ['./src/assets/fonts/nop/nop.ttf'],
dependencies: {
'react-native-vector-icons': {
platforms: {
ios: null,
android: null,
},
assets: VECTOR_FONTS.map((font) => VECTOR_ICONS_FONTS_PATH + '/' + font),
},
'react-native-flipper': {
platforms: {
ios: null,
android: __DEV__ ? undefined : null,
},
},
},
};
But if I do this I get the following error:
$ react-native run-android
error Unrecognized command "run-android".
info Run "react-native --help" to see a list of all available commands.
error Command failed with exit code 1.
What version of Nodejs and NPM are you using?
This can sometimes happen when the project is not initialized properly.
Perhaps try the following:
Update NodeJs/NPM if not LTS
Delete the node_modules directory
Run npm install or yarn install
Run react-native init
Run react-native run-android
(Duplicate of react-native run-android is unrecognized)

Error running tests with Detox in Expo React Native project

When I try to run my tests using detox in a React Native Expo project, I get the following error:
detox[18834] WARN: [Client.js/PENDING_REQUESTS] App has not responded to the network requests below:
(id = -1000) isReady: {}
That might be the reason why the test "Login workflow should have login screen" has timed out.
detox[18834] INFO: Login workflow: should have login screen [FAIL]
FAIL e2e/firstTest.e2e.js (137.697 s)
Login workflow
✕ should have login screen (120015 ms)
● Login workflow › should have login screen
thrown: "Exceeded timeout of 120000 ms for a hook.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
7 | });
8 |
> 9 | it('should have login screen', async () => {
| ^
10 | await expect(element(by.id('loginFormTitle'))).toBeVisible()
11 | });
12 |
at firstTest.e2e.js:9:3
at Object.<anonymous> (firstTest.e2e.js:8:1)
detox[18833] ERROR: [cli.js] Error: Command failed: node_modules/.bin/jest --config e2e/config.json '--testNamePattern=^((?!:android:).)*$' --maxWorkers 1 e2e
I am running iPhone 11 Pro simulator, and the expo app is already running in a separate server. I also have a Exponent.app in my /bin folder, which I downloaded from Expo's website. The logic in my test case doesn't require any timeout and it involves just a simple login screen.
Any solution for this error?
I had a similar issue using the latest version of Expo (v39).
It seems like the issue is that Detox waits for an app is ready event before running the tests, but this doesn’t get triggered with the most recent versions of the Expo SDK.
The solution I ended up with was to create a standalone build of the app and run Detox against that.
My .detoxrc.json looks like:
{
...,
"configurations": {
"ios": {
"type": "ios.simulator",
"build": "expo build:ios -t simulator",
"binaryPath": "bin/myapp.app",
}
}
}
As of December 2020, I'm using detox 17.14.3 with Expo 39.0.5 and I've managed to solve this issue without needing to use a standalone build. Patch and explanation provided below.
It turns out that detox-expo-helpers is overriding an environment variable (specifically SIMCTL_CHILD_DYLD_INSERT_LIBRARIES) to specify the path to the ExpoDetoxHook framework (provided by the expo-detox-hook package). Well, that update to the environment now happens too late in the cycle. It takes place when running Detox's reloadApp, but by then, Detox has started up Jest which has workers running with their own copy of process.env. Workers get a copy of process.env at the time of creation, and they are not shared with the parent process, so the changes this library makes to environment variables are not reflected inside Jest workers. The hook framework is not available to the running application, and so the Detox is stuck waiting for a ready signal that doesn't come.
Detox's launchApp for iOS simulator uses SIMCTL_CHILD_DYLD_INSERT_LIBRARIES to specify it's own library, but because the environment vars are isolated in the worker, it does not see the change this package makes. To solve this, I modified this library to expose the changes to process.env as a separate exported function, and then I call that much earlier in the test lifecycle to ensure it is set before any workers are started. Here is a patch compatible with patch-package.
# file patches/detox-expo-helpers+0.6.0.patch
diff --git a/node_modules/detox-expo-helpers/index.js b/node_modules/detox-expo-helpers/index.js
index 864493b..3147a55 100644
--- a/node_modules/detox-expo-helpers/index.js
+++ b/node_modules/detox-expo-helpers/index.js
## -45,7 +45,16 ## function resetEnvDyldVar(oldEnvVar) {
}
}
-const reloadApp = async (params) => {
+let initialized = false;
+let detoxVersion;
+let oldEnvVar;
+const init = () => {
+ if (initialized) {
+ return;
+ }
+
+ initialized = true;
+
if (!fs.existsSync(expoDetoxHookPackageJsonPath)) {
throw new Error("expo-detox-hook is not installed in this directory. You should declare it in package.json and run `npm install`");
}
## -56,12 +65,16 ## const reloadApp = async (params) => {
throw new Error ("expo-detox-hook is not installed in your osx Library. Run `npm install -g expo-detox-cli && expotox clean-framework-cache && expotox build-framework-cache` to fix this.");
}
- const detoxVersion = getDetoxVersion();
- const oldEnvVar = process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES;
+ detoxVersion = getDetoxVersion();
+ oldEnvVar = process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES;
if (semver.gte(detoxVersion, '9.0.6')) {
process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES = expoDetoxHookFrameworkPath;
}
+}
+
+const reloadApp = async (params) => {
+ init();
const formattedBlacklistArg = await blacklistCmdlineFormat(params && params.urlBlacklist);
const url = await getAppUrl();
## -121,5 +134,6 ## module.exports = {
getAppUrl,
getAppHttpUrl,
blacklistLiveReloadUrl,
+ init,
reloadApp,
};
With that in place, I modified my e2e/environment.js file to look like the following. The addition is the initExpo call. When it runs this early in the test lifecycle, the environment is modified before any workers are started, and as a result, calls to reloadApp no longer wait indefinitely.
/* eslint-disable import/no-extraneous-dependencies */
const { init: initExpo } = require('detox-expo-helpers');
const { DetoxCircusEnvironment, SpecReporter, WorkerAssignReporter } = require('detox/runners/jest-circus');
class CustomDetoxEnvironment extends DetoxCircusEnvironment {
constructor(config) {
super(config);
initExpo();
// Can be safely removed, if you are content with the default value (=300000ms)
this.initTimeout = 300000;
// This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
// This is strictly optional.
this.registerListeners({
SpecReporter,
WorkerAssignReporter,
});
}
}
module.exports = CustomDetoxEnvironment;