React Native 导入外部模块的钩子调用无效

dfuffjeb  于 2023-10-22  发布在  React
关注(0)|答案(4)|浏览(129)

我正在编写一个名为“Formcomponent”的包,使用React + React Bootstrap。
这是index.tsx

/**
 * Renders a component for a form.
 */
import React from "react";
import Form from "react-bootstrap/Form";

/**
 * List of props
 * @returns
 */
interface FormcomponentProps {
  name: string;
}

export const Formcomponent = ({ name }: FormcomponentProps) => {
  return (
    <Form.Group controlId={name}>
      {/* input text */}
      <Form.Label>{name}</Form.Label>
      <Form.Control type="text" name={name} />
    </Form.Group>
  );
};

当我构建它时,我得到了dist:
index.d.ts

/**
 * Renders a component for a form.
 */
import React from "react";
/**
 * List of props
 * @returns
 */
interface FormcomponentProps {
    name: string;
}
export declare const Formcomponent: ({ name }: FormcomponentProps) => React.JSX.Element;
export {};


index.js

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Formcomponent = void 0;
/**
 * Renders a component for a form.
 */
var react_1 = __importDefault(require("react"));
var Form_1 = __importDefault(require("react-bootstrap/Form"));
var Formcomponent = function (_a) {
    var name = _a.name;
    return (react_1.default.createElement(Form_1.default.Group, { controlId: name },
        react_1.default.createElement(Form_1.default.Label, null, name),
        react_1.default.createElement(Form_1.default.Control, { type: "text", name: name })));
};
exports.Formcomponent = Formcomponent;

我在package.json中导入另一个React项目作为"@sineverba/form-component": "file:../../personali/npm-pkg-form-component",,在React项目的App.jsx中使用它作为

import * as React from "react";
import { Form } from "react-bootstrap";
import { Formcomponent } from "@sineverba/form-component";

export const App = () => {
  return (
    <Form>
      <Formcomponent name="ciao" />
    </Form>
  );
};

export default App;

但我进了控制台

Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: [...]

Uncaught TypeError: Cannot read properties of null (reading 'useMemo') at FormGroup.js:11:26

(来自react bootstrap的FormGroup.js定义为:

import * as React from 'react';
import { useMemo } from 'react';
import FormContext from './FormContext';
import { jsx as _jsx } from "react/jsx-runtime";
const FormGroup = /*#__PURE__*/React.forwardRef(({
  controlId,
  // Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
  as: Component = 'div',
  ...props
}, ref) => {
  const context = useMemo(() => ({
    controlId
  }), [controlId]);
  return /*#__PURE__*/_jsx(FormContext.Provider, {
    value: context,
    children: /*#__PURE__*/_jsx(Component, {
      ...props,
      ref: ref
    })
  });
});
FormGroup.displayName = 'FormGroup';
export default FormGroup;

更新1

在index.tsx(外部组件)中,我将React导入为import * as React from "react";,但得到了相同的错误。

更新2

我在这里上传了repository:https://github.com/sineverba/npm-pkg-form-component
当然它没有发布(是的),为了测试它可以在本地克隆并与"@sineverba/form-component": "file:../../personali/npm-pkg-form-component"一起使用
一个小消息。我试图导出一个简单的<p>Hello World</p>(一个简单的HTML),只有当我使用Formcomponent而不是FormComponent作为名称时,它才能工作(请参阅C的大写字母)。
顺便说一下,将名称更改为Formcomponent并引入React Bootstrap Form,也会出现同样的错误。

更新3

我从我的组件中导出了一个简单的input标记,它可以工作。可能是从另一个组件导入的React Bootstrap库的一些奇怪错误。

gajydyqb

gajydyqb1#

首先检查React项目和Formcomponent库是否使用了React和React Bootstrap的兼容版本。不匹配的版本可能会导致像Invalid hook call这样的错误。
在你的项目中,查看package.json文件以找到你正在使用的React版本。它将列在dependencies部分下,例如,

"dependencies": {
    "react": "^18.2.0",
    "react-bootstrap": "^2.9.0",
    ...
}

类似地,在Formcomponent包中,查看其package.json文件以查找它使用的React版本,例如,

"dependencies": {
    "react": "^18.2.0",
    "react-bootstrap": "^2.9.0",
    ...
}

比较版本号以查看它们是否匹配。如果存在差异,可能会导致问题。
Formcomponent包中将ReactReact Bootstrap Decrypt为peer dependencies,以避免重复安装。这告诉你的库的消费者他们应该使用哪个版本的React和React Bootstrap,而不需要将它们包含在你的库包中。

"peerDependencies": {
  "react": "^18.2.0",
  "react-bootstrap": "^2.9.0"
}

您还可以运行npm ls reactyarn list react命令(取决于您使用的是npm还是yarn)。
此命令将列出项目中所有已安装的React版本以及它们的使用位置。
注意:项目中Formcomponent的import语句与Formcomponent包中的export语句不匹配。import语句是import { Formcomponent } from "@sineverba/form-component";,但在你的项目中,你试图将它用作<FormComponent name="ciao" />,而应该是<Formcomponent name="ciao" />(区分大小写)。
在开发过程中,考虑使用npm linkyarn link链接到本地Formcomponent包,而不是使用package.json中的文件路径。这将允许您在更真实的环境中测试库,类似于发布时的使用方式。
我从我的组件中导出了一个简单的input标记,它可以工作。
可能是从另一个组件导入的React Bootstrap库的一些奇怪错误。
如果导出一个简单的HTML input标签可以工作,但从React Bootstrap导出一个组件却不行,这表明React Bootstrap在Formcomponent包中的使用或集成可能存在问题。
如果React Bootstrap还没有在Formcomponent包中声明为对等依赖,那么这样做会很好。这样,Formcomponent将使用安装在消费项目中的React Bootstrap版本,这有助于避免版本冲突和重复依赖。

"peerDependencies": {
    "react-bootstrap": "^version"
}

并确保React Bootstrap组件在Formcomponent包中正确导入。仔细检查import语句,并将其与React Bootstrap的文档进行比较,以确保准确性。
查看控制台中提供的特定错误消息和堆栈跟踪,以查明错误的来源。这可以提供一些线索,说明使用React Bootstrap组件时会出现什么问题。

yv5phkfx

yv5phkfx2#

在您的Formcomponent.tsx中,从react-bootstrap导入Form和FormLabel并相应地使用它们:
tsx:

import React from 'react';
import { Form, FormControl, FormGroup } from 'react-bootstrap';

interface FormcomponentProps {
  name: string;
}

export const Formcomponent = ({ name }: FormcomponentProps) => {
  return (
    <FormGroup controlId={name}>
      <Form.Label>{name}</Form.Label>
      <FormControl type="text" name={name} />
    </FormGroup>
  );
};
zhte4eai

zhte4eai3#

当我创建一个包并尝试使用它时,我曾经像你一样遇到过这个问题。这是因为react声明在package.json中,你需要把它放在Formcomponent中的peerDependencies中,而不是dependenciesdevDependencies package.json中。是因为你将有另一个副本的React在您的应用程序中,导致error

"peerDependencies": {
    "react": "^18.2.0"
 }
z0qdvdin

z0qdvdin4#

这与项目加载两个React示例有关。
虽然目前大多数其他答案似乎都归咎于版本不匹配,但我认为这实际上是软件包解析的问题。
假设你正在观看两个不同的版本:
1.项目-这是你的入口点应用程序。
1.表单库-这是您在本地创建的可重用包。
这两个构建都将具有“/node_modules/react”。
当在package.json中使用“file:“来安装一个包时,它会在node_modules中创建一个链接,指向包的原始位置(开发位置)(Form Library)。
这意味着当您的项目构建解析包时,它将找到表单库的node_modules文件夹,并将其用作其包解析的一部分。
在解析包时,优先级顺序为:别名>本地节点模块>根节点模块。
这对你的项目意味着什么,当它为位于你的表单库中的文件解析包时,它将从“表单库/节点模块”中解析,如果它没有找到包,它将检查“项目/节点模块”。
这就是给你两个版本的React的原因:

  1. Project/node_modules/react
  2. Project/node_modules/Form Library/node_modules/react
    如果你使用任何捆绑的包分析器,你应该能够看到两个React示例被包括在内。
    为了解决这个问题,你可以同时开发你的Project和Form Library,你可以在你的构建中添加一个别名,并强制它将React解析为Project/node_modules/react。
    Webpack别名-https://webpack.js.org/configuration/resolve/#resolvealias
    ViteJs别名-https://vitejs.dev/config/shared-options.html#resolve-alias
    注意:任何共享的、具有单例或类似的包都需要别名,以避免问题。
    ViteJs Super Heavy Handed Example -项目配置:
// vite.config.ts
import react from '@vitejs/plugin-react-swc';
import Checker from 'vite-plugin-checker';
import { resolve } from 'path';
import { UserConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
const fs = require('fs-extra');

const shouldAnalyze = process.env.ANALYZE;

// Load Project package.json file.
let rawdata = fs.readFileSync('./package.json');

// parse as object.
let packageJson: any = JSON.parse(rawdata);

const resolveNodeModules: any[] = [];

// read through package.json file and create aliases for each dependency.
if (packageJson?.dependencies) {
    Object.keys(packageJson.dependencies).forEach(key => {
        resolveNodeModules.push({
            find: key,
            replacement: pathResolve('node_modules') + '/' + key
        });
    })
}

// read through package.json file and create aliases for each devDependency.
if (packageJson?.devDependencies) {
    Object.keys(packageJson.devDependencies).forEach(key => {
        resolveNodeModules.push({
            find: key,
            replacement: pathResolve('node_modules') + '/' + key
        });
    })
}

const config: UserConfig = {
    resolve: {
        alias: [
            {
                find: /@\//,
                replacement: pathResolve('src') + '/'
            },
            // add all previously created aliases from package.json to the build.
            ...resolveNodeModules
        ]
    },
    build: {
        modulePreload: {
            polyfill: false
        },
        rollupOptions: {
            plugins: !!shouldAnalyze ? [visualizer({ open: true, filename: './bundle-size/bundle.html' })] : [],
        },
        sourcemap: !!shouldAnalyze
    },
    define: {
        'process.env': process.env
    },
    plugins: [
        react({ 
        }),
        Checker({
            typescript: true,
            overlay: true
        })
    ]
}

const getConfig = () => config

export default getConfig

相关问题