I want to compile some kind of typescript component library to use in my main application. I work with a kind of monorepo structure and locally reference the component library from its directory. The actual setup, compiling of typescript lib and usage in my my main application do work fine.
However if the component imported from my lib uses any of @material-ui/core components in its jsx tree it will result in an error Error: 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: [...] error. Which i cannot really understand, as those components work fine when i simply copy the code over to main application without compilation in between.
So my best guess it has something to do with commonjs, es modules etc. But i am not really familiar with this topic and i do not know of this is really to blame or how to find a solution. @material-ui/core however should be considered a peerDependency and not compiled in the component library. This the compiled code will need to properly import it from the main apps dependencies. I am compiling my library as commonjs module.
Basic setup is
app/ #create-react-app here
package.json # with following contents
...
"dependencies": {
"@material-ui/core": "4.11.4",
"components": "file:../component-lib",
...
component-lib/
dist/
src/
index.tsx
package.json
tsconfig.json
A component that would trigger the error if put in index.tsx is (Note that replacing <Typography> with i.e. <span> would result in everything working just fine.
import {
Typography,
TypographyProps,
} from '@material-ui/core';
import React from 'react';
/**
* Properties for InfoLine component
*/
export type InfoLineProps = {
infos: Array<[React.ReactElement | string, React.ReactElement | string]>,
infoTypographyProps?: TypographyProps,
labelTypographyProps?: TypographyProps,
};
const InfoLine = ({
infos,
infoTypographyProps = { variant: 'body1' },
labelTypographyProps = { variant: 'body1', color: 'textSecondary' },
...stackProps
}: InfoLineProps) => {
return (
<div>
{infos.map(([icon, info]) => (
<>
{
typeof icon === 'string'
? (<Typography {...labelTypographyProps}>{icon}: </Typography>)
: icon
}
{
typeof info === 'string'
? (<Typography {...infoTypographyProps}>{info}</Typography>)
: info
}
</>
))}
</div>
);
};
export default InfoLine;
The tsconfig.json that is used when building via tsc:
{
"compilerOptions": {
"module": "commonjs",
"target": "es2015",
"allowJs": true,
"declaration": true,
"skipLibCheck": true,
"esModuleInterop": true,
"outDir": "./dist",
"jsx": "react-jsx"
},
"include": [
"src/**/*"
]
}