How to run React JS build in React-Native Webview? - reactjs

I created complete offline ReactJS web application and I want to run it from android application from Web View using React-Native.
I followed the following procedure to do so:
1. I created a compiled ReactJS web application got the build using the following command:
npm run build
Then I created react-native project and placed the build folder with following architecture
I updated App.js with the following content:
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, WebView} from 'react-native';
import {roscon} from "./build/index.html";
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={{height: 300, width: 300,overflow:'hidden' }}>
<WebView
source={{uri: roscon}}
scalesPageToFit={true}
domStorageEnabled={true}
javaScriptEnabled={true}
startInLoadingState={true}
/>
</View>
);
}
}
After running this code I expected it to run my ReactJS Web application, instead I got white screen.
Can you please tell what can be the causing issues and how i can make my ReactJS Web App run on react-native?
Note: I was able to run generated build folder using npm command
serve -s build
But I still can't figure out how to port it to react-native project as WebView

After research and testing, I found a solution.
The main issue i found was the compiled build folder is rendered as static html. And it needed a server to serve pages.
So, I followed this link for getting build project to get it up and running
Then, integrating it with nodejs Android Project Samples to get my build folder running in android as a Webview.
Note: I also tried react-snapshot and react-snap but they didn't gave satisfactory results.

Try to require the html file correctly and pass it in to source prop in this way:
<WebView
source={require('./build/index.html')}
/>

Install
npm install react-native-react-bridge
These are used to render React app in WebView
npm install react-dom react-native-webview
Requirements
react 16.8+
react-native 0.60+
Usage
Fix metro.config.js to use babelTransformer from this library.
module.exports = {
transformer: {
babelTransformerPath:
require.resolve('react-native-react- >.
.bridge/lib/plugin'),
...
},
};
Make entry file for React app. web.js
import React, { useState } from "react";
import {
webViewRender,
emit,
useSubscribe,
} from "react-native-react-bridge/lib/web";
const Root = () => {
const [data, setData] = useState("");
// useSubscribe hook receives message from React Native
useSubscribe((message) => {
if (message.type === "success") {
setData(message.data);
}
});
return (
<div>
<div>{data}</div>
<button
onClick={() => {
// emit sends message to React Native
// type: event name
// data: some data which will be serialized by JSON.stringify
emit({ type: "hello", data: 123 });
}}
/>
</div>
);
};
// This statement is detected by babelTransformer as an entry point
// All dependencies are resolved, compressed and stringified into one file
export default webViewRender(<Root />);
Use the entry file in your React Native app with WebView.
import React from "react";
import WebView from "react-native-webview";
import { useBridge } from "react-native-react-bridge";
import webApp from "./WebApp";
const App = () => {
// useBridge hook create props for WebView and handle communication
// 1st argument is the source code of React app
// 2nd argument is callback to receive message from React
const { ref, source, onMessage, emit } = useBridge(webApp, (message) => {
// emit sends message to React
// type: event name
// data: some data which will be serialized by JSON.stringify
if (message.type === "hello" && message.data === 123) {
emit({ type: "success", data: "succeeded!" });
}
});
return (
<WebView
// ref, source and onMessage must be passed to react-native-webview
ref={ref}
source={source}
onMessage={onMessage}
/>
);
};

Related

How to do Video Trimmer in Reactjs

I want to do a video trimmer in reactjs. I have searched and I got the package
Package name: npm i react-video-trimmer
Here is the example of this package,
Example code:
import React from "react";
import ReactVideoTrimmer from "react-video-trimmer";
import "react-video-trimmer/dist/style.css";
const Trimmer = () => {
const handleVideoEncode = React.useCallback(result => {
console.log("Encoding Result:", result);
});
return (
<div>
<ReactVideoTrimmer
onVideoEncode={handleVideoEncode}
timeRange={{ start: 10, end: 100 }}
/>
</div>
);
};
But, I don't know how to feed the video URL which I have to trim and I'm not clear about how this package is working.
So, please tell me how this package is working. And, Is there any alternative way to create a video trimer in Reactjs?

Invalid Hook Call - GetStreamChat API

Trying to install the GetStreamChat React JS API but getting:
Invalid hook call. Hooks can only be called inside of the body of a function component.
I followed the debug guide from https://reactjs.org/warnings/invalid-hook-call-warning.htmlbut still unable to figure it out.
The guide lists 3 commons causes but I went through all of them.
Common Cause 1: Mismatching Versions of React and React DOM?
I am using react-dom#16.13.1
Common cause 2: Breaking the Rules of Hooks?
This is how I'm implementing the functional component:
chatbox.js -> this is straight copied and pasted from their API tutorial guide. It works in an app all by itself.
import React from 'react';
import { Chat, Channel, ChannelHeader, Window } from 'stream-chat-react';
import { MessageList, MessageInput, MessageLivestream } from 'stream-chat-react';
import { MessageInputSmall, Thread } from 'stream-chat-react';
import { StreamChat } from 'stream-chat';
import 'stream-chat-react/dist/css/index.css';
const chatClient = new StreamChat('XYZ');
const userToken = 'XYZ';
chatClient.setUser(
{
id: 'crimson-rice-9',
name: 'Crimson rice',
image: 'https://getstream.io/random_png/?id=crimson-rice-9&name=Crimson+rice'
},
userToken,
);
const channel = chatClient.channel('livestream', 'spacex', {
image: 'image here',
name: 'Bruh Goes Boi',
});
const ChatBox = () => (
<Chat client={chatClient} theme={'livestream dark'}>
<Channel channel={channel} Message={MessageLivestream}>
<Window hideOnThread>
<ChannelHeader live />
<MessageList />
<MessageInput Input={MessageInputSmall} focus />
</Window>
<Thread fullWidth />
</Channel>
</Chat>
);
export default ChatBox;
testscreen2.js
import ChatBox from "../Chat/ChatBox"
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2);
const TestPage2 = () => {
return (
<div>
<p>Why you not work</p>
<ChatBox/>
</div>
)
}
export default TestPage2
Common Cause 3: Duplicate React
Running npm ls react produces:
starter-template#1.0.0 /Users/michaelninh/WebstormProjects/Shuriken
└─┬ stream-chat-react#2.2.0
└─┬ react-file-utils#0.3.15
└─┬ react-file-icon#0.2.0
└── react#16.13.1
Since I don't have multiple reacts showing, does this mean I only have one react copy? However when I do the other suggested test, it produces false which the guide says that I have two React copies:
// Add this in node_modules/react-dom/index.js
window.React1 = require('react');
// Add this in your component file
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2);
PRODUCES FALSE
If I do have multiple react copies, I am unsure how to debug since I'm a beginner at this.
Silly me. I installed the package in my project packages folder instead of the the client packages.

How do I access React component's local state using Cypress?

I'm using react with redux and testing with cypress,
I was able to access the store using
cy.window().its('store').invoke('getState').then((state) => {}
But how do i access a component's local state rather than the application store?
I tried
cy.get('.simple-component').its('getState')
or
cy.get('.simple-component').invoke('getState')
but Cypress is returning "CypressError: Timed out retrying: cy.invoke() errored because the property: 'getState' does not exist on your subject"
And on the Cypress console (in chrome) it's yeilding:
Yielded:
<div class="simple-component" getstate="[object Object]"></div>
It seems that's caused by React removing the methods from the DOM so i need to access it in React rather than in the DOM?
import React, { Component } from 'react';
class simpleComponent extends Component {
constructor(props) {
super(props)
this.state = {
sample: "hello"
}
}
// getState() just for testing on cypress
getState() {
return this.state
}
render(){
return <div className="simple-component" getState={this.getState()}></div>
}
}
As an alternative can i export the local component state at the end of the simple-component using window.store?
>= version 7.0.0
As of Cypress 7.0, the new Component Test Runner is now bundled with Cypress
From https://www.cypress.io/blog/2021/04/06/cypress-component-testing-react:
We still need to install the react adapter to mount components:
yarn add -D cypress #cypress/react #cypress/webpack-dev-server
add a glob pattern matching your component tests to cypress.json:
{
"component": {
"testFiles": "**/*.test.{js,ts,jsx,tsx}",
"componentFolder": "src"
}
}
Tell Cypress to use #cypress/webpack-dev-server for component tests. in cypress/plugins/index.js:
const injectDevServer = require("#cypress/react/plugins/react-scripts")
module.exports = (on, config) => {
injectDevServer(on, config)
return config
}
This will configure the Cypress Webpack Dev Server to use the same Webpack configuration as Create React App uses.
If you are using a different template, like Next.js, we have some other adapters available. It's also possible to create your own adapter.
< version 7.0.0
There's a Cypress Plugin for that, called react-unit-test. It gives you the ability to mount React components directly (adds a cy.mount() command) and provides access to the component's internal state.
Here's an example from the repo's readme:
// load Cypress TypeScript definitions for IntelliSense
/// <reference types="cypress" />
// import the component you want to test
import { HelloState } from '../../src/hello-x.jsx'
import React from 'react'
describe('HelloState component', () => {
it('works', () => {
// mount the component under test
cy.mount(<HelloState />)
// start testing!
cy.contains('Hello Spider-man!')
// mounted component can be selected via its name, function, or JSX
// e.g. '#HelloState', HelloState, or <HelloState />
cy.get(HelloState)
.invoke('setState', { name: 'React' })
cy.get(HelloState)
.its('state')
.should('deep.equal', { name: 'React' })
// check if GUI has rerendered
cy.contains('Hello React!')
})
})
You can identify the element without mounting the react component. If you are testing your react app in isolation with the source code or writing functional UI test cases, you can consider a Cypress plugin called cypress-react-selector. It helps you identify web elements by component, props, and state even after the minification. You need to use React Dev Tool to identify the component names in that case.
Here is an example:
Suppose your react app:
const MyComponent = ({ someBooleanProp }) => (
<div>My Component {someBooleanProp ? 'show this' : ''} </div>
)
const App = () => (
<div id='root'>
<MyComponent />
<MyComponent name={bob} />
</div>
)
ReactDOM.render(<App />, document.getElementById('root'))
Then you can simply identify the react element like:
cy.getReact('MyComponent', { name: 'bob' } ).getCurrentState();
Find more sample test here
Hope it will help!

How to use a Cordova plugin in react JS page

I would like to use some Cordova plugins in my react JS app and the app has been failing. I understand cordova is only available at runtime by i need a workaround.
My app was created with create react app with cordova here
For example, I want to import the cordova-plugin-device to get the device uuid with the following code:
import React, {Component} from 'react';
...
var device = require("cordova-plugin-device");
class Login extends Component {
handleSubmit = () => {
const { phone, password } = this.state
let params = {
phonenumber: phone,
password: password,
deviceID: device ? device.uuid : "test"
}
...
}
render () {
...
}
}
}
I am getting an error with npm start and when i run npm build . This is the error Module not found: Can't resolve 'cordova-plugin-device' in 'C:\projects\
Any pointers on how to implement this would be appreciated.
I figured how to solve my problem. COrdova is made available at runtime so i used window.cordovaPulgin to access it.
For example, i needed a Payment plugin service that call its methods, like this:
PaymentPlugin.pay(payRequest, paySuccess, payFail);
My problem was that my code npm start && npm run build was failing because it could not find PaymentPlugin, so to make this work, after extensive research, realized that if plugin was properly installed, I would be able to use it like
window.PaymentPlugin.pay(payRequest, paySuccess, payFail);
I was trying to follow the same suggested solution by #Lateefah to get access of local notifications plugin but window.cordova was showing undefined. After doing some more research, I realised that the solution is correct but my react application must reside in the the cordova project as suggest here https://stackoverflow.com/a/43440380/4080906.
The steps I followed:
create cordova app (e.g. cordovaApp)
cd cordovaApp && create-react-app reactApp
modify index.js in reactApp and add
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
const startApp = () => {
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
};
if (!window.cordova) {
startApp();
} else {
document.addEventListener("deviceready", startApp, false);
}
serviceWorker.unregister();
add cordova.js reference in the body tag of index.html in reactApp under public folder
<script type="text/javascript" src="cordova.js"></script>
(OPTIONAL) modify build command in package.json in reactApp to build and copy the build to cordova www folder (For Linux/Mac)
"build": "react-scripts build && cp -a ./build/. ../www/",
(OPTIONAL) To test this, I added a button in app.js and added notification there and it worked for me on an android device.
import React from "react";
import logo from "./logo.svg";
import "./App.css";
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<button
onClick={() => {
if (window.cordova) {
if (window.cordova.plugins) {
alert("Crodova and plugins Found");
window.cordova.plugins.notification.local.schedule({
title: "My first notification",
text: "Thats pretty easy...",
foreground: true,
});
} else alert("plugins not found");
} else alert("Cordova not found");
}}
>
Get Notified
</button>
</header>
</div>
);
}
export default App;

Need a workaround for Adobe SnapSVG-animator running in React.js

SnapSVG extension for Adobe Animate.cc 2017 is able to create interactivity and animations for the web. I'm currently trying to use an exported SnapSVG Adobe Animate.cc project in my REACT JS WebApplication.
What I did so far
Published html file from a SnapSVG project(Animate.cc 2017)
Copyed custom json file created from the SnapSVG project in animate.cc in my React app.
Installed SnapSVG from npm install in my React App.
Imported the js file copyed from the html publication created from animate.cc by importing the code. ( SnapSVG-animator isn't available in npm)
The custom json file from animate.cc/snap svg project is loaded async and will be added to the SVGAnim(SnapSVGAnimator.min.js) function object which will create the svg animation in de browser.
The code
import axios from 'axios';
import snapsvg from 'snapsvg';
import { SVGAnim } from './SnapSVGAnimator.min.js';
let jsonfile = "circle.json",
responseType: 'json';
componentDidMount(){
axios.get(jsonfile)
.then(response => {
const json = response.request.responseText;
const animatedSVG = new SVGAnim(json);
});
}
Problem
The SnapSVGAnimator.min.js creates warnings and errors when it's imported in the JSX file. Looks like something going wrong with compiling these code.
✖ 1557 problems (110 errors, 1447 warnings)
First, install the following libraries:
npm install --save snapsvg-animator snapsvg-cjs
given your json is at ../../public/myAnimation.json do the following:
import React, { useEffect } from 'react';
import SVGAnim from "snapsvg-animator";
import "snapsvg-cjs";
const myAnimation = require('../../public/myAnimation.json'); //import your json
function Home() {
const svg = new SVGAnim(
myAnimation,
200, //width
220, //height
40 //fps
);
useEffect(() => {
const container = document.getElementById('animation');
container.appendChild(svg.s.node);
});
return (
<div id="main">
<div id="animation"/>
</div>
)
};
export default Home;
I am using react hooks because it is all the rage ☺ but it's working with "render" and "componentDidMount" all the same.

Resources