I face issues with react-native-reanimated:
import Animated from 'react-native-reanimated';
console.log(Animated.onChange) // returns undefined
I'm using https://reactnavigation.org/docs/material-top-tab-navigator/ and receive the following error when running my app:
TypeError: onChange is not a function. (In 'onChange(_this.gesturesEnabled, cond(not(_this.gesturesEnabled), call([_this.gesturesEnabled], _this.toggleEnabled)))', 'onChange' is undefined)
This error is located at:
in Pager (created by TabView)
in RCTView (at View.js:34)
in View (created by TabView)
in TabView (at MaterialTopTabView.tsx:51)
in MaterialTopTabView (at createMaterialTopTabNavigator.tsx:44)
in MaterialTopTabNavigator (at Screen/index.tsx:21)
It's no issue with the library itself (I can see that the library exports the onChange method as part of the Animated namespace correctly), however I have a feeling that something's wrong with the metro configuration for my react native project (or typescript or babel config, I'm a little lost). Honestly I'm no expert with metro or babel, so any hint is highly appreciated.
package.json
{
"name": "project",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "react-native start",
"test": "jest --runInBand --detectOpenHandles",
},
"dependencies": {
"@react-native-community/async-storage": "^1.12.1",
"@react-native-community/geolocation": "^2.0.2",
"@react-native-community/masked-view": "^0.1.10",
"@react-native-firebase/analytics": "^8.0.1",
"@react-native-firebase/app": "^9.0.0",
"@react-native-firebase/auth": "^9.3.5",
"@react-native-firebase/crashlytics": "^8.5.2",
"@react-native-firebase/messaging": "^8.0.1",
"@react-native-firebase/perf": "^7.4.12",
"@react-navigation/material-bottom-tabs": "^5.3.9",
"@react-navigation/material-top-tabs": "^5.3.9",
"@react-navigation/native": "^5.8.9",
"@react-navigation/stack": "^5.12.6",
"@welldone-software/why-did-you-render": "^6.0.0-rc.1",
"axios": "^0.21.0",
"babel-plugin-wildcard": "^6.0.0",
"color": "^3.1.3",
"deepmerge": "^4.2.2",
"equentry-frontend-shared": "^2.0.3",
"i18next": "^19.8.3",
"lodash": "^4.17.20",
"metro-config": "^0.64.0",
"react": "17.0.1",
"react-i18next": "^11.7.3",
"react-native": "0.63.3",
"react-native-gesture-handler": "^1.8.0",
"react-native-localize": "^2.0.0",
"react-native-maps": "^0.27.1",
"react-native-paper": "^4.4.0",
"react-native-reanimated": "^1.13.1",
"react-native-safe-area-context": "^3.1.9",
"react-native-screens": "^2.14.0",
"react-native-svg": "^12.1.0",
"react-native-swipe-gestures": "^1.0.5",
"react-native-tab-view": "^2.15.2",
"react-native-vector-icons": "^7.1.0",
"react-redux": "^7.2.2",
"redux": "^4.0.5",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3"
},
"devDependencies": {
"@babel/cli": "^7.12.1",
"@babel/core": "^7.12.3",
"@babel/runtime": "^7.12.5",
"@react-native-community/eslint-config": "^2.0.0",
"@testing-library/dom": "^7.26.6",
"@testing-library/react-hooks": "^3.4.2",
"@testing-library/react-native": "^7.1.0",
"@testing-library/user-event": "^12.2.2",
"@types/color": "^3.0.1",
"@types/jest": "^26.0.15",
"@types/lodash": "^4.14.165",
"@types/react": "^16.9.56",
"@types/react-native": "^0.63.35",
"@types/react-native-vector-icons": "^6.4.6",
"@types/react-redux": "^7.1.11",
"@types/react-test-renderer": "^16.9.3",
"babel-jest": "^26.6.3",
"babel-plugin-i18next-extract": "^0.8.2",
"detox": "^17.11.4",
"eslint": "^7.13.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"git-branch-is": "^4.0.0",
"husky": "^4.3.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.6.3",
"jest-circus": "^26.6.3",
"jest-junit": "^12.0.0",
"jscpd": "^3.3.19",
"madge": "^3.12.0",
"metro-react-native-babel-preset": "^0.64.0",
"react-native-bundle-visualizer": "^2.2.1",
"react-native-svg-transformer": "^0.14.3",
"react-test-renderer": "17.0.1",
"typedoc": "^0.19.2",
"typedoc-plugin-external-module-name": "^4.0.3",
"typescript": "^4.0.5"
}
}
metro.config.js (I'm using react-native-svg-transformer in my project):
const { getDefaultConfig } = require('metro-config');
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts },
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
},
};
})();
tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"jsx": "react",
"lib": ["es6"],
"moduleResolution": "node",
"noEmit": true,
"strict": true,
"target": "esnext",
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"alwaysStrict": true,
"noUnusedLocals": true /* Report errors on unused locals. */,
"noUnusedParameters": true /* Report errors on unused parameters. */,
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
"skipLibCheck": true /* Skip type checking of declaration files. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
"exclude": [
"node_modules",
"babel.config.js",
"metro.config.js",
"jest.config.js",
"jest.setup.js",
"i18n"
]
}
babel.config.js
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
'i18next-extract',
{
locales: ['en', 'de'],
defaultNS: 'default',
customUseTranslationHooks: [['./src/hooks', 'useTranslation']],
outputPath: './i18n/translations/{{locale}}/{{ns}}.json',
defaultValue: '<translate me>',
useI18nextDefaultValue: true,
},
],
[
'wildcard',
{
exts: ['json', ''],
nostrip: true,
noModifyCase: true,
},
],
],
};
Any hint is highly appreciated, this is giving me headaches. :x
Thanks!
Ok, I figured it out by comparing a fresh react native app with only react-native-reanimated installed and my original project.
As expected the issue was with the babel configuration, especially the wildcard plugin. I couldn't exactly figure out the reason, but applying the plugin to the whole project caused some side effects, i.e. import * as abc from './somelocation
in my node_modules not being treated correctly.
As I need the wildcard plugin only to be applied to a specific folder ./src/i18n/*
I changed my babel.config.js
as follows:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
'i18next-extract',
{
locales: ['en', 'de'],
defaultNS: 'default',
customUseTranslationHooks: [['./src/hooks', 'useTranslation']],
outputPath: './src/i18n/translations/{{locale}}/{{ns}}.json',
defaultValue: '<translate me>',
useI18nextDefaultValue: true,
},
],
],
overrides: [
{
test: './src/i18n/*',
plugins: [
[
'wildcard',
{
exts: ['json', ''],
nostrip: true,
noModifyCase: true,
},
],
],
},
],
};