Cross platform component react-native-web - react-native

I use react-native-web and I would like to create cross-platform components. I would like to differentiate each components by platform.
So I followed this tutorial: https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
I have 3 files :
     - friendsList.android.js
     - friendsList.ios.js
     - friendsList.web.js
And for import FriendsList, in the index.android.js, index.ios.js index.web.js :
Import FriendsList from './friendsList';
On Ios and Android, it works fine. But on the web, it does not recognize the file. I actually have 3 solutions:
- specify when importing: import FriendsList from './friendsList.web';
- define a service who dispatch for each platform
- or to define an alias in webpack:
resolve: {
alias: {
'react-native': 'react-native-web',
'./friendsList': './friendsList.web',
},
},
Is there a way to import .web without this ways ?
Maybe it's not thought to do like that ?

According to react-native-web getting started documentation, you need to set the extensions.
resolve: {
// Maps the 'react-native' import to 'react-native-web'.
alias: {
'react-native': 'react-native-web'
},
// If you're working on a multi-platform React Native app, web-specific
// module implementations should be written in files using the extension
// `.web.js`.
extensions: [ '.web.js', '.js' ]
}

Related

The requested module '/node_modules/.vite/deps/vue.js' does not provide an export named 'default'

The following is my problem.
I packaged my project through vite in library mode. The error occurs whenever my library includes any third party UI library (e.g vue-loading-overlay). But other libraries like moment.js will have no problem.
This is my vite.config.js, Is there any problem with my configuration?
import { defineConfig } from "vite";
import vue from "#vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: resolve(__dirname, "src/lib.ts"),
name: "my-ui-lib",
fileName: "my-ui-lib",
},
rollupOptions: {
external: ["vue"],
output: [
{
format: "es",
exports: "named",
globals: { vue: "vue" },
},
],
},
},
});
Finally I resolved my problem, Adding the following in vite.config.js. It works for me.
build: {
/** If you set esmExternals to true, this plugins assumes that
all external dependencies are ES modules */
commonjsOptions: {
esmExternals: true
},
}
Original Answer
"Chart.js V3 is treeshakable so you need to import and register everything or if you want everything you need to import the chart from the auto import like so:
change
import Chart from 'chart.js'
to ->
import Chart from 'chart.js/auto';
For more information about the different ways of importing and using chart.js you can read the integration page in the docs.
Since you are upgrading from Chart.js V2 you might also want to read the migration guide since there are some major breaking changes between V2 and V3"
/* Adding the following in vite.config.js. Just copy and paste all these code. It works for me. */
import { defineConfig } from "vite";
import react from "#vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
commonjsOptions: {
esmExternals: true,
},
});
react-pdf v6 has a pretty clever solution for this, look at their entry files. I think the point is to link to the correct file, somehow there's no need to "actually" import the worker (it doesn't run on main thread anyway I guess? New to worker and pdfjs).
import * as pdfjs from 'pdfjs-dist/build/pdf';
pdfjs.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.js', import.meta.url);
import.meta availability.
Refer to vuejs 3 documentation to import vue.

How to mock NativeModules in react-native project by js side?

I am working on runnning my react-native app with expo, the problem I am facing is to mock the NativeModules exported by "react-native", my current solution is using babel-plugin-module-resolver plugin to redirect the "react-native" module to my "react-native-proxy" module, which exports a proxy object that expanding NativeModules.
Everything goes well untill I want to use expo-file-system to mock our native FileSystem api, expo-file-system is an es module, an error look like caused by module mixed use occured. I tried import * as FileSystem from 'expo-file-system'; in babe.config.js, "react-native-proxy" module, metro.config.js, both throwed an error.
How could I require an es module in babel.config.js or babel-plugin?
Or any idea of achieving the target mentioned above?
Thanks.
Solved by myself.
As I know that, the NativeModules object exported from "react-native" is created in Libraries/BatchedBridge/NativeModules.js, so I change my mock target to this file. I create a file name "NativeModulesProxy.js" which content copied from "Libraries/BatchedBridge/NativeModules.js", then use babel-plugin-module-resolver plugin to redirect the src file to this one.
// babel.config.js
const resolvePath = require('babel-plugin-module-resolver').resolvePath;
const path = require('path');
...
plugins: [
[
'module-resolver',
{
resolvePath(sourcePath, currentFile, opts) {
if (sourcePath.includes('NativeModules')) {
const sourceAbsolutePath = path.resolve(path.dirname(currentFile), sourcePath);
if (sourceAbsolutePath.endsWith('node_modules/react-native/Libraries/BatchedBridge/NativeModules')) {
return path.resolve(__dirname, 'NativeModulesProxy');
}
}
return resolvePath(sourcePath, currentFile, opts);
}
}
]
],
...
NativeModules object is a quote of global.nativeModuleProxy, base on this, I could create my own proxy object like "global.myNativeModuleProxy" which combines global.nativeModuleProxy and my mock native modules.

Shorter Way for URLs with vscode on react native

So I have a react native project, and in that project many of my urls start looking like this: import Component from '../../component/file';
So after this problem I saw this video by fireshipio with says I can shorten it by adding a jscofig.json file but it did not work when I wrote import Component from '../../component/file';
it just told me it could not find the path please tell me what I am supposed to do to make this working because if its possible my links will become so much shorter and smarter. Remember the programming rule do not repeat yourself so please help me follow that.
link to fireshipio vid: https://www.youtube.com/watch?v=WpgZKBtW_t8
You should Modify/Add your desired common path in babel.config.js file and then you can easily import any file/class without adding long paths
Here is an example of babel.config.js from one of my project.
module.exports = api => {
api.cache(true);
return {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
'#babel/plugin-proposal-optional-chaining',
'#babel/plugin-proposal-nullish-coalescing-operator',
[
'module-resolver',
{
root: ['./src'],
alias: {
'#routes': ['./src/routes.js'],
'#navigations': ['./src/navigations'],
'#components': ['./src/components'],
'#store': ['./src/store'],
'#images': ['./src/images'], //You can add your source path like this
'#utils': ['./src/utils'],
},
},
],
],
};
};
After adding the source path in babel.config.js you can import the files like this in your class.
import SampleImage from '#images/sampleImage.png'
You can import like this in your any class, No need to do '../../src/image/sampleImage.png'

Import yaml file in React Native results in number 1 instead of actual content

I'm trying to import a yaml file in React Native. I can see in the Metro defaults.js file that yaml is listed as an asset extension already.
The imported value is always the number 1 though and not the actual contents of the yaml file.
import enYaml from '../i18n/locale/en.yaml';
That is because you're loading it as a resource. So it's the resource ID. What you'd need is an answer for What is the equivalent of a webpack loader when using the metro bundler in React Native?
To do this in Expo which uses babel.config.js which Metro uses you need to add the babel-plugin-content-transformer as a dev dependency and configure it as follows
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
[
'content-transformer',
{
transformers: [
{
file: /\.ya?ml$/,
format: 'yaml',
},
],
},
],
...

Use optimized es6 build of MobX for React Native in Metro config

I'm trying to use the optimized, es6 build of Mobx, as per the documentation:
Tip: the main entry point of the MobX 5 package ships with ES5 code for backward compatibility with all build tools. But since MobX 5 runs only on modern browsers anyway, consider using the faster and smaller ES6 build: lib/mobx.es6.js. For example by setting up a webpack alias: resolve: { alias: { mobx: __dirname + "/node_modules/mobx/lib/mobx.es6.js" }}
https://mobx.js.org/README.html#browser-support
This allows me to import mobx and get the mobx.es6.js build:
import mobx from 'mobx' // Yay, es6 build!!!
This works great for Webpack-based projects, such as Electron ones, where I already have it working.
For React Native, I can specify extraNodeModules in metro.config.js like so:
module.exports = {
resolver: {
extraNodeModules: {
"mobx": path.resolve(__dirname, 'node_modules/mobx/lib/mobx.es6.js'),
},
},
};
...except that doesn't work, I presume, because the mobx dependency resolves fine on its own, and so this configuration option is never checked.
I can use a separate alias for mobx, such as mobx-es6 but that's not ideal, to put it nicely:
module.exports = {
resolver: {
extraNodeModules: {
// Nooo I don't want to update a bazillion source files!.
"mobx-es6": path.resolve(__dirname, 'node_modules/mobx/lib/mobx.es6.js'),
},
},
};
Is there some other way to configure Metro so that I can override the mobx import like I can with Webpack?
I'm using RN 0.60.0.
The solution is to add a browser section to package.json:
"name": "My React Native Project",
"version": "0.0.1",
"browser": {
"mobx": "mobx/lib/mobx.es6.js"
},
This is undocumented, AFAICT, but there are hints here:
resolverMainFields
Type: Array<string>
Specify the fields in package.json files that will be used by the module resolver to do redirections when requiring certain packages. For example, using ['browser', 'main'] will use the browser field if it exists and will default to main if it doesn't.
https://facebook.github.io/metro/docs/configuration#resolvermainfields
import mobx from 'mobx' // Yay, es6 build!!!