How to re-use a React library with hooks - reactjs

I'm writing my own library to help building forms with React.
The goal is to be able to re-use it in any React application (made from scratch, with create-react-app, nextjs, other ?)
My problem is that I can't firgure out how to re-use it.
Here is the first issue I encountered when I tried to reuse my library in another project :
Invalid Hook Call Warning
React says that the reason could be one of the following :
You might have mismatching versions of React and React DOM.
You might be breaking the Rules of Hooks.
You might have more than one copy of React in the same app.
After investigation, I've excluded points 1. and 2.
I was using Parcel in the first place to build my library but I needed to exclude React from the bundle to solve 3.
That's why I switched to Webpack and moved the React dependency to the peerDependencies + devDependencies.
I've tried different webpack configurations :
const config: webpack.Configuration = {
...
externals: {
react: 'React'
}
}
--> which leads to the following error in the consuming application :
React is not defined
const config: webpack.Configuration = {
...
externals: {
react: {
root: 'React',
amd: 'react',
commonjs: 'react',
commonjs2: 'react',
umd: 'react',
}
}
}
--> which leads to the following error :
Cannot read property 'createContext' of undefined
I've tried to use my library with npm link and also by publishing it on npm but the result is the same.
Step to reproduce :
Install #mlaopane/formhook in your react application (npm i #mlaopane/formhook or yarn add #mlaopane/formhook)
Try to use a component, for example
import { Form } from '#mlaopane/formhook'
export default function App() {
return <Form />
}
You should see the error in the console
Thank you for helping me with this :)
NB: This is my first issue, please tell if more information is needed.

I've finally found a solution, inspired by create-react-library.
It looks like Rollup is a better candidate for bundling React libraries.
Even if I still can't link my library locally, it can be used when published.

Related

Vite, NPM, React Component Library Invalid hook call, externals problem?

I was able to bundle my React Component library in Rollup, but I wanted the features of Vite for development and installed in over the weekend. My problem is that now I'm getting the following error when I try to npm link my vite generated distribution with another react probject.
Basically it's saying that it can't use useContext when it gets the 'Provider" which is really just a react context. It seems like it's having a problem here in the bundle when it tries to load it:
var Context=/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__.createContext(null);
My vite config looks as such:
export default defineConfig({
plugins: [react(), dts({ insertTypesEntry: true })],
build: {
lib: {
entry: path.resolve(__dirname, "src/lib/index.ts"),
name: "MyLib",
formats: ["umd", "es"],
fileName: (format) => `my-lib.${format}.js`,
},
rollupOptions: {
external: [ "react", "react-dom" ]
}
},
});
Searching said that it might be a problem with my dependencies, using two versions of react or react-dom. I've tried it with every dependency configuration I can think of and it all breaks in different ways. I think maybe npm cacheing could be confusing me or something though.
Have any ideas? Vite works fine in 'dev' mode, and the components were working ok in Rollup so I feel like it's just a dumb configuration thing I don't understand

create-react-app can't find react in tests when aliased with preact

I have created a project with "create-react-app". I have added preact to the project by ejecting it and using alias in webpack config.
alias: {
'react': 'preact-compat-enzyme',
'react-dom': 'preact-compat-enzyme',
'react-dom/server': 'preact-render-to-string',
'react-addons-test-utils': 'preact-test-utils',
'react-addons-transition-group': 'preact-transition-group'
}
This works in the code and I am able to render the app. But, in tests I am getting the following error
Cannot find module 'react' from 'App.test.js'
Why can't it find the module? Is there something that we have to add to config other than alias?
This took some digging but, I got it working by adding following in in moduleNameMapper package.json.
"moduleNameMapper": {
"^react$": "preact-compat-enzyme",
"^react-dom$": "preact-compat-enzyme",
"^react-dom/server$": "preact-render-to-string",
"^react-addons-test-utils$": "preact-test-utils",
"^react-addons-transition-group$": "preact-transition-group",
},

Multiple copies of React in my Application

I am using react-treelist component and inside that i am using office-ui-fabric callout(its like a tooltip) component to have a tooltip on elements in tree structure when i am running this program it runs correctly but when i am bundling and using the bundle file in my project as a module it gives error:-
Exception in Layer.componentDidUpdate(): Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded
I have tried many solutions from diff forum but none of them worked.I am sure problem is of multiple copies of react.
Details of what i did
I cloned react-treelist from github then i installed office-ui-fabric module then i changed some code to make callout component work with react-treelist, i did npm start it worked fine the i did npm run dist and copied the bundle file to my project directory inside a folder named react-treelist and then imported that file as module in my code and ran the code it gave me the above error.
Can anybody suggest me how to accomplish the above task without having multiple copies of react in my application
If you use webpack to build your application, you can use the resolve.alias configuration option to make sure that all modules refer to the same react library:
resolve: {
alias: {
'react': fs.realpathSync('node_modules/react'),
'react-dom': fs.realpathSync('node_modules/react-dom')
}
}
If you use web pack then you can fix it by adding the following to Webpack config
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}

Prevent web pack from bundling react

How do I prevent webpack from bundling react?
Currently I am writing a library that causes a You've loaded two copies of React on the page. error after distribution. I suspect that webpack starts bundling all dependencies, including devDependencies.
Is there any way around that?
In my case it should be possible for the library to get React out of node_modules.
So what I basicly want is, instead of webpack resolving the require('React), it should just leave require('React) untouched.
You can use webpack externals.
externals: {
// Use external version of React
"react": "React"
}
UPD Detailed docs on the resulting code generated for externals.
To make webpack "leave require('React) untouched" you need the following config
{
output: { libraryTarget: 'commonjs' },
externals: { react: true }
...
}
Moelalez, just like Yury Tarabanko stated, externals option allows you import an existing API into applications. For context, say you want to use React from a CDN via a separate tag and still declare it as a dependency via require("react") in your application, you would use externals option to specify that.

What does 'Only a ReactOwner can have refs.' mean?

I have a simple react component with a form in it:
var AddAppts = React.createClass({
handleClick: function() {
var title = this.refs.title.getDOMNode().value;
....
var appt = {
heading: title
...
}
CalendarActions.addAppointment(appt);
},
render: function() {
return (
<div>
<label>Description</label>
<input ref="title"></input>
...
</div>
);
}
});
module.exports = AddAppts;
I am trying to render this component in another react component:
var AddAppt = require('./AddAppts');
render: function() {
return (
<div>
<AddAppt />
</div>
);
}
but I get this error:
Uncaught Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.
I have googled it, but cannot figure out what I need to do to fix this issue.
This is FYI for people using react and redux-devtools who are getting OP's message. Your structure looks like
project
client
node_modules
react
redux-devtools
node_modules
react
The code in client will require the first react module; that in redux-devtools will require the other react. The react module keeps state and assumes it has all the state.
You get the OP's message because your state is split between the 2 react modules. This situation is what I believe #asbjornenge refers to.
I was bundling the client code with webpack, so I used that to handle the issue. You can force all require and import to always use the first react by adding the following to webpack.config.js
module.exports = {
...
resolve: {
alias: {
'react': path.join(__dirname, 'node_modules', 'react')
},
extensions: ['', '.js']
},
...
);
I have not looked into what I would need to do if the situation occurred with unbundled code running on node.
I encountered this error when a component module I was using had it's own react dependency installed. So I was using multiple versions of React.
Make sure NOT to list react under dependencies in your package.json.
This is why we have peerDependencies ;-)
Just in case. Be aware of the React module name you are using (it is case-sensitive). I've got the same error when by coincidence I tried to require React dependency with different names in two separate modules.
//module1.js
var React = require('react');
...
//module2.js
var React = require('React');
....
It works and even renders something but the Only a ReactOwner can have refs... error appears.
Rearranging the script resolved the issue.
Wrong.
<script src="./lib/react.js"></script>
<script src="./lib/react-dom.js"></script>
<script src="./lib/react-with-addons.js"></script>
Correct
<script src="./lib/react.js"></script>
<script src="./lib/react-with-addons.js"></script>
<script src="./lib/react-dom.js"></script>
Reference https://github.com/gcanti/tcomb-form/issues/107#issuecomment-150891680
I saw this error while developing a react module that was linked to a test project using npm link. Switching to a regular dependency solved the problem.
It seems that React doesn't like npm link.
There is another situation.
I set the React as external library in the webpack.config.js and import react.js from cdnjs.
externals: {
'react': 'React'
}
When I using an ui library depend on React,such as material-ui,react-bootstrap,this issue occurred.
am writing with my old pen. make sure in your project root package.json move react dependency as early as possible. still you are getting issue? & if you are using npm modules and grunt task, you can add below clean task to remove inner components react(duplicates).
clean: {
react : ['node_modules/**/react','!node_modules/react']
},
I saw this error after I moved my package.json file up a level, so I had 2 node_modules directories in my project (one in ./node_modules and another in ./web/node_modules). Removing the old directory fixed the problem.
For me the reason for the same problem was that I've imported the ReactDom globally, as a property of the window object, like this:
import ReactDOM from 'react-dom'
window.ReactDOM = ReactDOM
removing window.ReactDOM = ReactDOM fixed the problem.
Similar to this answer, I was seeing this error while using a separate module that I had been developing in a separate directory using yarn link.
The solution was to run yarn unlink module-name in my project's working directory. I then removed node_modules and ran yarn upgrade module-name and yarn for good measure.
Instead of
<input ref="title"></input>
Try this
<input ref={(title) => this.title = title}></input>
None of the given solutions worked for me, though they certainly helped show me where to look.
For some reason my project has two node_modules folders - one in the root directory and one a level up. I'm new to React so I don't know if this is normal or what. I'm just going with what Visual Studio gave me when I started a new project.
Anyway, I knew which module was causing the problem, and it happened to only exist in one of the node_modules folders.
I moved the problem module over to the other node_modules folder. Problem solved.

Resources