react-native-reanimated:onChange 不是函数

react-native-reanimated: onChange is not a function

我遇到 react-native-reanimated 问题:

import Animated from 'react-native-reanimated';

console.log(Animated.onChange) // returns undefined

我正在使用 https://reactnavigation.org/docs/material-top-tab-navigator/ 并在 运行 我的应用程序时收到以下错误:

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)

库本身没有问题(我可以看到库将 onChange 方法正确导出为 Animated 命名空间的一部分),但是我感觉我的反应本机项目的 metro 配置有问题(或 typescript 或 babel 配置,我有点迷路了)。老实说,我不是 metro 或 babel 方面的专家,所以非常感谢任何提示。

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(我在我的项目中使用了 react-native-svg-transformer):

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,
      },
    ],
  ],
};

非常感谢任何提示,这让我很头疼。 :x

谢谢!

好的,我通过比较一个只安装了 react-native-reanimated 的新 React Native 应用程序和我的原始项目来弄明白了。

不出所料,问题出在 babel 配置上,尤其是通配符插件。我无法完全弄清楚原因,但是将插件应用于整个项目会产生一些副作用,即我的 node_modules 中的 import * as abc from './somelocation 没有得到正确处理。

因为我只需要将通配符插件应用于特定文件夹./src/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: './src/i18n/translations/{{locale}}/{{ns}}.json',
        defaultValue: '<translate me>',
        useI18nextDefaultValue: true,
      },
    ],
  ],
  overrides: [
    {
      test: './src/i18n/*',
      plugins: [
        [
          'wildcard',
          {
            exts: ['json', ''],
            nostrip: true,
            noModifyCase: true,
          },
        ],
      ],
    },
  ],
};