React-native-web Multi-Platform Setup using Expo 44 + Typescript - react-native

What is the simplest way to implement Multi-Platform Setup for a component in Expo. I have tried mamy diferent ways.. it was working on web but it is failing on Native and failing with Jest & #testing-library/react-native. Ideally I would like the least amount of custom config etc (do not want to eject). I expect the file structure to look like this:
Component
|- index.tsx
|- Component.native.tsx
|- Component.web.tsx
I am not sure how to do the index.tsx. I saw someone say something like this would work:
// index.tsx
// #ts-ignore
export { default } from "Component"
this didn't work so I did
// index.tsx
// #ts-ignore
export { default } from "./Component"
This worked for web, but the jest test said
Cannot find './Component'
However, Jest was able to find:
'./Component.mobile.tsx'
'./Component.web.tsx'
I tried:
// index.tsx
// #ts-ignore
import Component from "./Component";
export default Component
and the tests was the same
and the native emulator said:
Unable to resolve module ./Component
I tried using lazy loading but this does not work on web.
import { lazy, Suspense } from "react";
import { Platform } from "react-native";
import Loading from "../../components/Loading";
import { ComponentType } from "./types";
const Web = lazy(() => import("./Component.web"));
const Mobile = lazy(() => import("./Component.mobile"));
const Component: ComponentType = (props) => {
const isWeb = Platform.OS === "web";
return (
<Suspense fallback={<Loading message="Loading Component" />}>
{isWeb ? <Web {...props} /> : <Mobile {...props} />}
</Suspense>
);
};
export default Component
Questions
how to use diferent files for components depending on platform (exlude other files from build)
how to make it ok with ts in vscode
Using Expo 44. Thanks

I would use named exports. So begin by having the same component name in both files. Next I would have one file called Component.tsx and the other Component.native.tsx. That will be enough to allow the bundler to pull the native for native and the other for non-native (in other words web). That should be all you need.

Related

How to use hooks in custom plugins

I am trying to write a custom plugin for our Docusaurus site. I am able to wire up the custom component, but I cannot use hooks like useState or useEffect. The page crashes saying I'm using an invalid React hook.
I know its possible to use hooks because I see other plugins doing it so I'm sure its a syntax problem somewhere.
Here's my code:
index.ts
import path from 'path'
module.exports = function () {
return {
name: 'docusaurus-theme-myorg-technology',
getThemePath() {
return path.resolve(__dirname, './theme')
}
};
};
theme/index.tsx
import React from 'react'
import {CustomTOC} from './CustomTOC'
const WrappedTOC = (props: any) => {
return (
<CustomTOC {...props} />
);
};
export default WrappedTOC;
theme/CustomTOC.tsx
import React, { useState } from 'react';
import TOC from '#theme-init/TOC';
export default function CustomTOC(props: any) {
//const [tags, setTags] = useState<any[]>([]); <-- if I comment this out the page crashes
return (
<>
<TOC {...props} />
Hello world
</>
);
}
"Invalid hooks call" link to a doc page, that you should read carefully.
Most likely: you are using a different version of React for your component lib that the one Docusaurus uses internally, and it leads to the React lib being used twice at runtime. Make sure the final project will only include one React version. You can for example use the exact same version that the one Docusaurus uses

Expo web build fails because of JSX in .js files

I have a Expo project to which I added victory-native library. When building for the web, Webpack complains about missing loader. The errors are of this pattern below and appear for all the files from this particular library
./node_modules/victory-native/src/components/victory-clip-container.js 10:22
Module parse failed: Unexpected token (10:22)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|
| export default class extends VictoryClipContainer {
> static defaultProps = Object.assign({}, VictoryClipContainer.defaultProps, {
| groupComponent: <G />,
| rectComponent: <Rect />,
How do I add the correct loader? Do I add something to the babel config? Or should I override the webpack configuration?
Babel is currently using only babel-preset-expo
As stated by Michael, native and web code can be differentiated using the naming in the files.
A simple complete solution is:
victory.js:
import * as Victory from 'victory';
export default Victory;
victory.native.js:
import * as Victory from 'victory-native';
export default Victory;
And when you want to use the victory:
import Victory from "./victory"; // this will default to victory.js or victory.native.js
// depending on the compilation target platform.
const VictoryBar = Victory.VictoryBar;
const VictoryChart = Victory.VictoryChart;
const VictoryTheme = Victory.VictoryTheme;
...
{
...
return <View style={styles.container}>
<VictoryChart width={350} theme={VictoryTheme.material}>
<VictoryBar data={data} x="quarter" y="earnings" />
</VictoryChart>
</View>
}
Whilst using victory-native in Expo apps that target iOS & Android is fully supported, we do not support building for the web with victory-native.
However, as both victory-native and victory share the same public API, it's possible to configure your Expo project to automatically use victory-native when building your native apps for iOS & Android, and victory when building your web app.
yarn add -D #expo/webpack-config
Then, create a webpack.config.js file at the root of your Expo project
const createExpoWebpackConfigAsync = require('#expo/webpack-config');
module.exports = async function(env, argv) {
const config = await createExpoWebpackConfigAsync(env, argv);
// resolve victory-native as victory for the Web app
config.resolve.alias['victory-native'] = 'victory';
return config;
};
Refered from the official documentation .
You cannot use victory-native imports for web and you cannot use victory import for react native.
I solved the issue by creating an file named victory.native.ts and victory.ts which containing all necessary imports.
victory.native.ts:
import * as Victory from 'victory-native'
victory.ts:
import * as Victory from 'victory'
Now you can import victory.ts in web and app.

How to integrate inertiaJS with quasar framework?

I would like to integrate intertiaJS into my Quasar app so that I can communicate with my Laravel backend. My problem now is that the general stuff is taken over by the Quasar CLI, which is good in principle, but in this case it takes away my entry point as described at https://inertiajs.com/client-side-setup:
import { createApp, h } from 'vue'
import { App, plugin } from '#inertiajs/inertia-vue3'
const el = document.getElementById('app')
createApp({
render: () => h(App, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: name => require(`./Pages/${name}`).default,
})
}).use(plugin).mount(el)
My thought is that I could use a boot file like the offered in Quasar (https://quasar.dev/quasar-cli/boot-files), but I have to admit that I don't have the right approach.
When I look at the app.js that is automatically generated, I see that nothing special happens in the rendering:
/**
* THIS FILE IS GENERATED AUTOMATICALLY.
* DO NOT EDIT.
*
* You are probably looking on adding startup/initialization code.
* Use "quasar new boot <name>" and add it there.
* One boot file per concern. Then reference the file(s) in quasar.conf.js > boot:
* boot: ['file', ...] // do not add ".js" extension to it.
*
* Boot files are your "main.js"
**/
import Vue from 'vue'
import './import-quasar.js'
import App from 'app/src/App.vue'
import createStore from 'app/src/store/index'
import createRouter from 'app/src/router/index'
export default async function () {
// create store and router instances
const store = typeof createStore === 'function'
? await createStore({Vue})
: createStore
const router = typeof createRouter === 'function'
? await createRouter({Vue, store})
: createRouter
// make router instance available in store
store.$router = router
// Create the app instantiation Object.
// Here we inject the router, store to all child components,
// making them available everywhere as `this.$router` and `this.$store`.
const app = {
router,
store,
render: h => h(App)
}
app.el = '#q-app'
// expose the app, the router and the store.
// note we are not mounting the app here, since bootstrapping will be
// different depending on whether we are in a browser or on the server.
return {
app,
store,
router
}
}
I.e. in principle I should be able to link in without it causing any conflict situations. The question is, how would that look?
I have to link into the rendering afterwards and overwrite it as described in the code example. I would like to stay with the Quasar Cli, because it is very useful and the situation described here is the only exception.
p7
the boot files is the right place to inject and initialize your own dependencies or just configure some startup code for your application.
I have not had the opportunity to use the library you mention, but I detail a little how you could implement
create your boot file
import { plugin } from '#inertiajs/inertia-vue';
export default async({ app, Vue }) => {
Vue.use(plugin);
}
until there you have 50%. On the other hand, you cannot do a mixin to the main instance but you could do it for each page, however I recommend that you make a component part to which you add the data you need and make a mixin of the library you need
<template>
<div />
</template>
<script>
import { App } from '#inertiajs/inertia-vue';
export default {
mixins: [App],
props: ['initialPage', 'resolveComponent'],
}
</script>
In order to do this, modify according to how the library you use works.

How to configure App.js in react-native to use React-native-ui-kitten?

I am a newbie in react-native and I'm trying to use the react-native-ui-kitten library. The problem is that the documentation is not really helpful.
I have I have installed ui-kitten and the theme as indicated with the command: npm i react-native-ui-kitten #eva-design/eva
The documentation asks to configure the application root which I consider to be the App.js file unless I'm wrong.
So i changed the App.js generated code to this:
import React from 'react';
import {
mapping,
theme,
} from '#eva-design/eva';
import { ApplicationProvider } from 'react-native-ui-kitten';
import { Application } from './path-to/root.component';
export default class App extends React.Component {
public render(): React.ReactNode {
return (
<ApplicationProvider
mapping={mapping}
theme={theme}>
<Application/>
</ApplicationProvider>
);
}
}
Unfortunetely it's not working.
Has anyone recently used ui-kitten library ?
What does the documentation mean by Application Root and how to set up a simple react-native project to use react-native-ui-kitten?
And yes I actually checked the documentation but maybe there is something I am not doing right.
I ran into the same problem.
I discovered that the creators of this UI kit use in fact in their code examples Typescript.
So you need to recreate your Reactnative project using a Typescript template, then rename accordingly the App.js into App.tsx
Any other components need to be renamed with the extension .tsx.
Make sure you read about Typescript:
https://www.typescriptlang.org
Here it is how you can recreate your project with Typescript:
https://facebook.github.io/react-native/blog/2018/05/07/using-typescript-with-react-native
All the best,
I am also a beginner in react-native and it seems we kinda differ in implementing the code. I am not sure if this would help but this is the way I implement your code:
import * as React from 'react';
import { mapping, light as lightTheme } from '#eva-design/eva';
import { ApplicationProvider } from 'react-native-ui-kitten';
import { Application } from './path-to/root.component';
const App = () => (
<ApplicationProvider
mapping={mapping}
theme={lightTheme}>
<Application/>
</ApplicationProvider>
);
export default App;
You could try it and let me know if this suits you. Good luck!

How to not put "use strict" everywhere

I'm trying to write some tests for a React app I've been working on, and I figured I would use Jest since it's mentioned in the React docs.
I'm using Webpack and so far I've installed jest-cli, babel-jest, and I included the following configuration in package.json:
"jest": {
"scriptPreprocessor": "./node_modules/babel-jest",
"unmockedModulePathPatterns": [
"./node_modules/react",
"./node_modules/react-dom"
],
}
So, I'm writing the tests for some file foo.js. This file includes some other module bar.js (i.e. const bar = require('./bar');). Unfortunately, when I run jest I get the following error:
SyntaxError: Block-scoped declarations (let, const, function, class) not yet
supported outside strict mode in file 'js/foo.js'.
So, after some research, I find out I have to include 'use strict'; at the top of foo-test.js. However, for some reason, I still get the same error unless I also include 'use strict'; at the top of foo.js.
So my question is: am I doing something wrong? If not, is there anyway for me to write my tests using Jest without having to write 'use strict'; at the top of all my source files?
It seems to test out basic ES2015 classes with jest, use strict is required, however to test React Components, 'use strict' isn't required. Here's an example
//CheckboxWithLabel.js
import React, {Component} from 'react';
class CheckboxWithLabel extends Component {
constructor(){
super(...arguments);
this.state = {
isChecked: false
};
this.onChange = this.onChange.bind(this);
}
onChange() {
this.setState({
isChecked: !this.state.isChecked
});
}
render() {
return (
<label>
<input type="checkbox"
checked={this.state.isChecked}
onChange={this.onChange} />
{this.state.isChecked ? this.props.labelOn : this.props.labelOff }
</label>
);
}
}
export default CheckboxWithLabel;
//CheckboxWithLabel_tests.js
jest.disableAutomock(); //use this instead of jest.autoMockOff()
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import CheckboxWithLabel from '../source/components/CheckboxWithlabel';
// const CheckboxWithLabel = require('../source/components/CheckboxWithLabel');
describe('CheckboxWithlabel', () => {
const shallowRenderer = TestUtils.createRenderer();
//render a checkbox
const checkbox = TestUtils.renderIntoDocument(
<CheckboxWithLabel labelOn="On" labelOff="Off" />
);
// shallowRenderer.render(<CheckboxWithLabel labelOn="On" labelOff="Off" />)
// const checkbox = shallowRenderer.getRenderOutput();
// it('defaults to unchecked and off label', () => {
// const inputField = checkbox.props.children[0];
// const textNode = checkbox.props.children[1];
// expect(inputField.props.checked).toBe(false);
// expect(textNode).toEqual('Off');
// })
var checkboxNode = ReactDOM.findDOMNode(checkbox);
// let checkboxElement = TestUtils.findRenderedDOMComponentWithTag(checkbox, 'input');
it('defaults to Off label', () => {
expect(checkboxNode.textContent).toEqual('Off');
// expect(checkboxElement.checked).toBe(false);
});
})
Edited: This is not required anymore
Notice the only caveat being that you have to explicitly add a auto_mock_off.js file that simply adds this line (it took me hours to figure this one out)
jest.autoMockOff();
More information can be found on this thread on github Github React Issue #932
That's it! the component testing works perfectly. I've also tried the same example with shallow rendering and it worked perfectly too! Hope this helps!