In a react-native app that is also targeting web & Tizen, I'm trying to use webpack aliasing to replace a subcomponent of react-native-web with a custom package.
Relevant webpack config looks as follows:
module.exports = {
//...
resolve: {
alias: {
'react-native': 'react-native-web',
'react-native/Libraries/Alert': 'web/MyOwnAlertComponent', // does not work
'react-native-web/dist/exports/Alert': 'web/MyOwnAlertComponent', // does not work either
...
}
}
};
The first alias successfully aliases all react-native imports towards react-native-web. The Alert component is also part of react-native, and is imported in code as:
import { Alert } from 'react-native';
In react-native-web however, this Alert component is only a stub with no implementation. So in the second line of my webpack aliases I wanted to alias the react-native Alert towards my own implementation. This does not seem to work like this.
So how can I point react-native/Libraries/Alert or 'react-native-web/dist/exports/Alert towards my own Alert implementation? I'd like to resolve this, if possible, without touching the original code, which is shared between Android, iOS, tvOS, web & Tizen.
Related
I'm working on Next.js and React-Native-Web. I managed to run them together following the official Next.js example but when I'm trying to use the Animated package from the react-native it fails with Error that the requestAnimationFrame isn't defined. Basically this functionality does the node_modules package but I set the alias in webpack to translate all react-native requires to the react-native-web so even the node_modules package should use the react-native-web.
Any suggestions on how to solve it?
ReferenceError: requestAnimationFrame is not defined
at start (...node_modules\react-native-web\
dist\cjs\vendor\react-native\Animated\animations\TimingAnimation.js:104:11)
enter code here
Thanks for any help!
The problem is in the missed RequestAnimationFrame functionality at the server. This error happens when Next.js tries to render the component during SSR.
Unfortunately, there is no polyfill, etc. for such purpose so I just decided to use the Next.js dynamic imports for a Component that has animation functionality.
Next.js Official documentation
My own case оust to show how code looks:
import dynamic from 'next/dynamic';
const AutocompleteDropdown = dynamic(
() => import(
'myAwesomeLib/components/dropdown/autocomplete/AutocompleteDropdown'
),
{
ssr: false,
}
);
Now you can use the AutocompleteDropdown as the standard JSX component
I'm coding an App with React Native Web and NextJS 12, and in 2021 I encounter this problem and I fixed it, but now I know my fix was only for Next Dev, because it returned for Next Production Build.
Solution details:
No Dynamic import (which is useful too, but can be annoying when having lot of components using it)
Using RAF polyfill and Webpack ProvidePlugin.
Main thing to have in mind is that next.config.js with webpack 5 is going to check the codes first before even reach next entry points _documents.js and _app.js. It means that, you can put polyfill in those entry point files, it will still raise error of RAF undefined. You have to make requestAnimationFrame ready for config check.
DEV approach that will work on Next DEV only. Install RAF package https://www.npmjs.com/package/raf and In next.config.js add codes:
const raf = require('raf');
raf.polyfill();
This will add requestAnimationFrame and cancelAnimationFrame function to global and window object if they don't have it. In our case, it would add it in global for NodeJS.
But this solution won't work when executing npm run dev. I don't know why, if anyone knows why Next or Webpack 5 act differently from DEV to PRODUCTION, let me know.
Complete Solution:
Use ProvidePlugin config of webpack 5 https://webpack.js.org/plugins/provide-plugin/ . Create a file to use as modules, let's say: raf.js in root project or anywhere you want:
const raf = require('raf');
const polys = {};
raf.polyfill(polys);
module.exports = polys.requestAnimationFrame;
And in next.config.js use it inside webpack: () = {} like:
webpack: (config, options) => {
// console.log('fallback', config.resolve.fallback);
if (options.isServer) {
// provide plugin
config.plugins.push(
new options.webpack.ProvidePlugin({
requestAnimationFrame: path.resolve(__dirname, './raf.js'),
}),
);
}
And now, it's up to you to adapt to your existing config logic. By doing this, in Production Build, NextJS is injecting the requestAnimationFrame function in Server Side everywhere a module is using it.
Hello every one i just want to know the mechanism behind how importing react from 'react' works in my cra-app but for my component i have to import it by defining the path of the component file in a nutshell why is there a difference between thee two statements
import React, { Component } from 'react';
import Button from './Button';
Thanks in advance
This is because React is using Webpack internally to resolve modules. In the first import import React, { Component } from 'react'; webpack will look for the library in the node_modules folder as it has a resolver configured to do so.
In the second case you need to mention the path or alias the path ./Button with a shorter name like 'button' to tell Webpack where to search/resolve in that directory inside the webpack.config.js.
For an app created using create-react-app, the webpack.config.js will be located in node_modules/react-scripts/config/webpack.config.js.
There you would notice this resolver is defined which tells Webpack where to look for the core libs:
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules'].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
...
}
To alias your own path you can define a new alias in that file located in the config/webpack.config.js after you eject the app with npm run eject (you cannot undo this):
resolve: {
alias: {
'components' : path.resolve(__dirname, '../src/Components')
}
}
};
In you component you can import like:
import Button from 'components/Button';
react is a package installed via npm into node_mudules and can be imported by the package name.
Button is your custom component, and thus has to be imported by its path. If you made Button into a package, then you could install it via npm as well.
I have spent a lot of time looking into this, but to no avail. I am aware of how code splitting and dynamic bundling works in Webpack using the import promise API.
Howevr, my use case is that I have two completely separate bundles, generated separately using different webpack builds. To give you perspective, I am building React components and there is a requirement to dynamically load a react component into the page that has been compiled in a different process. Is this possible in react? I do have control over both webpack builds, so I can exclude dependencies, etc.
Update: I just looked at Vue.js, and how it allows developers to register Vue.js components and then reference them later in the code. I could potentially load my Vue.js component scripts before my page script. I'm trying to see if I can do something similar in React.
Did I understand you correctly: you have essentially got
a library of custom React components (built by Webpack build #1)
a React app that needs to use some (all) of these components (built by Webpack build #2, totally separate from #1)
?
If yes, then read on.
The "Is this possible in react?" question should instead be "Is this possible in Webpack?", and the answer is "Yes". The following is tested with Webpack 2, but should also work with v.1.
Let's call your projects Lib (your React component library) and App (the library consumer).
In the Lib project:
Create an entry point file, say index.js, that exports all the custom React components like this:
import {Button} from './button';
import {DatePicker} from './DatePicker';
import {TextBox} from './textBox';
export const MyComponentLib = {
Button,
DatePicker,
TextBox
};
Update webpack.config.js to make the project's bundle a UMD library (could also be 'var'), and set the entry point to the above index.js file. Doing so will make your library available via a global variable named MyComponentLib (the name comes from the export above) in the consuming app later on:
...
output: {
path: './dist',
filename: 'mylib.bundle.js',
libraryTarget: 'umd'
},
...
entry: './index.js',
...
On to the App project:
In the index.html file you will have two <script> tags: one for mylib.bundle.js (the output of the Lib project), and another for the bundle of the App project itself. You might have more bundles (app, vendor etc.), I'm just simplifying things here.
Update webpack.config.js to mark the component library as external dependency. Here, MyComponentLib is, again, the name of the global variable the library is available at, and myComponents is the name to use in import statements:
...
externals: {
myComponents: 'MyComponentLib'
},
...
Now, in App you can import a component like this:
import {DatePicker} from 'myComponents';
This will dynamically load DatePicker from the component library at run time via the global variable.
Bonus: if you use eslint, you don't want it to complain about missing modules that you know are external; add this to your .eslintrc:
...
"settings": {
"import/core-modules": ["myComponents"]
},
...
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.
I use react-native-web to have components that can be used not only mobile but also in web browsers. My understanding of the idea is simple: Provide an HTML-based implementation for the regular react-native components.
If I understand the library correct, it works by overwriting the package with webpack:
// webpack.config.js
module.exports = {
...
resolve: {
alias: {
'react-native': 'react-native-web'
}
}
};
But what if I want to provide implementations for components that are not implemented by react-native-web? And how could I provide an implementation for external packages like react-native-button.
A package like react-native-button is just a collection of code that uses React Native's API. If you load react-native-web as a stand-in for the original implementation, the third-party package might "just work". How well it works depends on how complete react-native-web's implementation of the API is.
Note that some third-party React Native packages include specific non-Javascript code (iOS/Android native code) that react-native-web does not aim to implement. If the package documentation says that you need to run react-native link or change your Xcode/Android Studio project configuration, it's probably not readily usable with react-native-web.