Packing js file to be inserted into React Native's WebView - react-native

I am creating a webview in a react native script, such as
<WebView source="https://www.google.com" />
However, I would like to inject a whole bunch of javascripts into the webview. The scripts to be included is located at let's say additionalScripts.js (which may import a whole bunch of dependencies such as jquery etc.
So intuitive I am trying to do
import additionalScripts from './additionalScripts.js'
<WebView source="https://www.google.com" injectedJavaScript={additionalScripts} />
Firstly it does not work.
Secondly, how do it make sure that the additionalScripts imported has been properly 'webpacked' and 'uglified' ?
Thanks

The problem is that injectedJavaScript needs a string, but the import actually executes the JS file and exports the result as an object.
To load the file as text, you can try something like React Native FS module, I learned about it from this answer.
// ...
import RNFS from require('react-native-fs');
// ...
class MyComponent extends Component {
constructor() {
super();
this.state = {
additionalScripts: null
};
}
componentDidMount() {
RNFS.readFile('./additionalScripts.js', 'utf8')
.then((contents) => {
this.setState({
additionalScripts: contents
});
});
}
render() {
if(!this.state.additionalScripts) {
return null;
}
return <WebView
source="https://www.google.com"
injectedJavaScript={additionalScripts} />;
}
}

Related

How to load local audio files dynamically with expo av

i have a bunch of audio files local to my app and i want to load them dynamically based on a component's state, the only way i found to load the audio with expo av is to use "require", but this method keeps returning "invalid call" whenever i try to use a variable of any sort or any template literals in the path string in it.
i tried even storing the paths in a json file and then referrirng to the path directly there and still got the invalid call error.
const { sound } = await Audio.Sound.createAsync(require(audioPaths['paths'][fileKey]), {}, playbackStatusUpdate);
how do you guys go about this issue? my files are local so i can't take advantage of streaming/loading them from network. does expo av offer any alternative to the require method? i need any tips or advice you might have
PS: if you need any more details about the situation please ask me and i will fill you in
Edit: this is how my paths json looks like
{
"paths": [
"../assets/Records/1.mp3",
"../assets/Records/2.mp3",
"../assets/Records/3.mp3",
"../assets/Records/4.mp3"
]
}
The issue is related to audio paths not being declared as System.registerDynamic.
you should define paths in JSON like this
"paths": [
require('./assets/one.mp3'),
require('./assets/two.mp3'),
require('./assets/three.mp3'),
]
}
and call this without require,
const { sound } = await Audio.Sound.createAsync(audioPaths['paths'][fileKey], {}, playbackStatusUpdate);
here is a snack I used
Theoretically when you want to upload files in a react native app, you will use either formData, or fileupload or react-native-fs or expo-file-system.
I recommend you the expo-file-system since you use expo.
See complete implementation here
But saying i have a bunch of audio files local to my app means that your audio files are already uploaded into a directory in your project folder and just you want those audio to be played dynamically using the expo-av Audio.Sound.createAsync() with require(). This is how I would do that:
import * as React from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import { Audio } from 'expo-av';
export default function App() {
const [sound, setSound] = React.useState();
async function playSound() {
console.log('Loading Sound');
const { sound } = await Audio.Sound.createAsync( require('./assets/Hello.mp3')
);
setSound(sound);
console.log('Playing Sound');
await sound.playAsync();
}
React.useEffect(() => {
return sound
? () => {
console.log('Unloading Sound');
sound.unloadAsync();
}
: undefined;
}, [sound]);
return (
<View style={styles.container}>
<Button title="Play Sound" onPress={playSound} />
</View>
);
}
This sample is for playing one audio, but in your question you want the audio to be played dynamically. For that you can only use react-native useEffect hook to create a kind of repeatable actions. I would first create a method playSound like this:
playSound = async () => {
await Audio.Sound.createAsync( require('' + source);
};
Here source is the path to an audio sent as variable and you may want to use function goToNext() and resumePlayList() to change the path of source variable like:
const goToNext = () => {
for(let i = 0; i < noGuest; i++){
source = JsonPath[i];
}

compiled application not loading in real device

For the life of me, I can't figure it out. All it shows is spinning without end and i am confused on the order of the life cycle happening. Basically, it goes to login or home screen and it works correctly on emulator but not on real device. I am on react 16.8.6 and react-native 0.60.5 environment.
I am getting started with RN and my debugging tools are not great. But for now just used Alert to see and the logic that was supposed to redirect to login/home screen is never reached. The Alerts shown are in the following order:
BS
mount2
render
mount1
My code is below: if the token exists, load home screen. else load auth screen is what I wanted to achieve but for now the line:
this.props.navigation.navigate(!goToLogin ? 'App' : 'Auth');
is never reached and so, spins a lot. Any help?
import React, {Component} from 'react';
import {StatusBar, View, Alert} from 'react-native';
import {
getUserToken,
loggedInToAssociation,
extractToken,
} from '../shared/loggedinUser';
import {setLanguage} from '../shared/localization';
import {appOptions} from '../config';
import Spinner from '../components/Spinner';
export default class AuthLoadingScreen extends Component {
constructor() {
super();
this.state = {
languageLoaded: false
};
}
componentDidMount() {
Alert.alert("mount1","oumnt1") // shown
loggedInToAssociation()
.then(details => {
// details is an array now
setLanguage(details['language']);
this.setState({languageLoaded: true});
Alert.alert("mount2","oumnt2") // SHOWN
})
.catch(err => {
setLanguage(appOptions.defaultLanguage);
this.setState({languageLoaded: true});
Alert.alert("mount3","oumnt3")
});
}
// Fetch the token from storage then navigate to our appropriate place
_bootstrapAsync = async () => {
const userToken = await getUserToken();
Alert.alert("bs","bs") // SHOWN
const tokenInfo = extractToken(userToken, 'both');
let goToLogin = true; // force user to go to the login page
if (tokenInfo.length == 2) {
goToLogin = false;
}
Alert.alert("bs2","bs2") // NEVER SHOWN
this.props.navigation.navigate(!goToLogin ? 'App' : 'Auth');
};
// Render any loading content that you like here
render() {
if (this.state.languageLoaded){
this._bootstrapAsync().then(s=>{
console.log(s)
}).catch(e=>{
console.log(e)
})
}
return (
<View>
<Spinner />
<StatusBar barStyle="default" />
</View>
);
}
}
did you check your debug console when running on device? There might be an unhandled promise rejection. The promise didn't go through but nowhere to handle the catch (consider try-catch scenario for this context).
It might be having a problem with this method.
extractToken(userToken, 'both')

How to Implement UPI Payment in my React-Native-App with Expo?

I want to add UPI payment in my App and currently using expo tool to create the app but now I got stuck as there is unable to find any direct implementation by using js only and require me to eject from expo. If there is any way by which I can implement the UPI without exiting from expo as it expo makes things really easy.
I have tried to implement the simple code provided in the docs for react-native-upi-payment but it is not moving ahead and require me to move through android manifest which is not inside the expo project.
import React from 'react'
import RNUpiPayment from 'react-native-upi-payment'
import { View, StyleSheet, Container } from 'react-native'
import { Input, Button } from 'react-native-elements'
import { Constants } from 'expo'
export default class PaymentScreen extends React.Component {
static navigationOptions = {
header: null
}
goToBhimUPI = () => {
RNUpiPayment.initializePayment({
vpa: 'someupi#ybl', // or can be john#ybl or mobileNo#upi
payeeName: 'Name',
amount: '1',
transactionRef: 'some-random-id'
}, this.successCallback, this.failureCallback);
}
failureCallback = (data) =>{
console.log(data)
}
successCallback = (data) => {
console.log(data)
}
render() {
return(
<Button
containerStyle = {styles.button}
type = 'clear'
title = 'Bhim UPI'
onPress = {this.goToBhimUPI}
/>
)
}
}
I expect this module to take me to the UPI payment gateway and return to the place from where it is called. Currently this is giving me error:(undefined is not an object(evaluating 'UpiModule.intializePayment'))
you can do this using react-native-upi-payment for all types of upi available or else phonepesdk for only phonepe integration

React native RNFS library

I have used react native RNFS library to access my mobile's file system . I am able to delete the file using file name. For example,
import React, { Component } from "react";
import { Text, View } from "react-native";
var RNFS = require("react-native-fs");
var path = RNFS.ExternalDirectoryPath + "/abc.png";
export default class HelloWorldApp extends Component {
render() {
return (
RNFS.unlink(path)
.then(() => {
console.log("FILE DELETED");
console.log(path);
})
.catch(err => {
console.log(err.message);
console.log(path);
})
);
}
}
Here, file with name abc.png will get deleted.
Question 1 - But suppose if want all files with a particular extension (like .txt,.png) to get deleted, then how can i achieve that ??
Question 2- using this code, Although I am able to delete the file but i am getting error in console .
Invariant Violation: Objects are not valid as a React child (found:
object with keys {_40, _65, _55, _72}). If you meant to render a
collection of children, use an array instead.
in HelloWorldApp (at renderApplication.js:34)
in RCTView (at View.js:45)
in View (at AppContainer.js:98)
in RCTView (at View.js:45)
in View (at AppContainer.js:115)
in AppContainer (at renderApplication.js:33)
I have used this documentation for writing code - https://github.com/itinance/react-native-fs
Delete files of a specific extension
It is possible to delete files with a specific extension it is a little involved but it is not too complicated. There are a few steps that we need to accomplish to make it happen.
Get a list of all the files in the directory
Filter the list so that only the files with the extension that we want to delete are left.
Unlink the files that are in our list.
So we could do something like this:
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, Button} from 'react-native';
const RNFS = require('react-native-fs');
export default class App extends Component<Props> {
deleteFiles = async () => {
let ext = 'txt'; // extention that we want to delete
var re = /(?:\.([^.]+))?$/; // regex to get the file extension of the file
try {
let path = RNFS.DocumentDirectoryPath;
let fileList = await RNFS.readDir(path); // get all the files in the directory
if (fileList.length) {
let filesToDelete = fileList.filter(file => {
let extension = re.exec(file.name)[1] // gets the extension of the found file
return extension === ext; // check to make sure that each file has the extension that we are looking for
});
for (const file of filesToDelete) {
if (file.isFile()) { // only delete if it is a file
try {
await RNFS.unlink(file.path);
console.log(`Successfully deleted ${file.name}`);
} catch (deleteError) {
console.log(`Error deleting ${file.name}`);
}
}
}
}
} catch (err) {
console.warn(err);
}
}
render() {
return (
<View style={styles.container}>
<Button title={'delete files'} onPress={this.deleteFiles} />
</View>
);
}
}
This is a simple component that renders a button on screen. The deleteFiles function is called when the button is pressed. Currently it is set up to delete all files with the extension txt
Error: Invariant Violation: Objects are not valid as a React child
You are calling the function inside the return of your render method. You shouldn't be doing that. Either implement something like I have above where the files are deleted on the press of a button or the function to delete is called via a specific action, if you need to delete the file when the component is created then you should be doing that inside the componentDidMount rather than in the render function.
componentDidMount () {
RNFS.unlink(path)
.then(() => {
console.log("FILE DELETED");
console.log(path);
})
.catch(err => {
console.log(err.message);
console.log(path);
});
}
You can use readdir to get the file names and then pass the ones you want to delete to unlink
This error is because you are not returning a component. The render() method expects a react component as its return value.
When you return something from render(){} it need to beJSX, you cant render RNFS, because it's not JSX.
return (
RNFS.unlink(path)
.then(() => {
console.log("FILE DELETED");
console.log(path);
})
.catch(err => {
console.log(err.message);
console.log(path);
})
);
I'm not sure what you want to render, but that is why you are getting this error.
Objects are not valid as a React child means "you can't render an Object", to render something, it need to be a JSX
(e.g <Text>hey<Text>)

React Native Expo.Font.loadAsync doesn't load Material Icons

I have a React Native, React hybrid app. For React Native i am using react-native-elements.
My app is run using Expo and was built out with the react-native init. I am getting the Material Icons (missing) RSD;
Through much searching, i have found the #expo/vector-icons but it doesn't seem to work. My App.js looks like this;
import React from 'react'
import { Font, AppLoading } from 'expo'
import { MaterialIcons } from '#expo/vector-icons'
import HybridApp from './src/App'
export default class NativeApp extends React.Component {
constructor() {
super()
this.state = {
fontsAreLoaded: false
}
}
async componentWillMount() {
await Font.loadAsync(MaterialIcons.font)
this.setState({ fontsAreLoaded: true })
}
render() {
const { fontsAreLoaded } = this.state
return !fontsAreLoaded ? <AppLoading /> : <HybridApp />
}
}
As you can see, i am waiting for the font to load... all to no avail.
After hours wracking my brain on this, the answer was there in front of me the whole time.
Presumably, React Native Elements refers to Material icons as Material Icons, not MaterialIcons.
This means that the default import from #expo/vector-icons does not work as their reference to Material icons is different.
The solution is to manually select Material icons from expo, replacing this line;
await Font.loadAsync(MaterialIcons.font)
with
await Font.loadAsync({
'Material Icons': require('#expo/vector-icons/fonts/MaterialIcons.ttf')
})
I hope this saves someone some time in the future.
The icons are actually fonts and must first be loaded. It seems they are autoloaded sometimes and others times are not.
So to ensure they are loaded, do this:
import FontAwesome from './node_modules/#expo/vector-icons/fonts/FontAwesome.ttf';
import MaterialIcons from './node_modules/#expo/vector-icons/fonts/MaterialIcons.ttf';
...
async componentWillMount() {
try {
await Font.loadAsync({
FontAwesome,
MaterialIcons
});
this.setState({ fontLoaded: true });
} catch (error) {
console.log('error loading icon fonts', error);
}
}
...
render() {
if (!this.state.fontLoaded) {
return <AppLoading />;
}
Then when you reference the type, it must be the same type that the component you are using is expecting.
For example, react native elements expects these types: material-community, font-awesome, octicon, ionicon, foundation, evilicon, simple-line-icon, zocial, or entypo
See complete answer here:
http://javascriptrambling.blogspot.com/2018/03/expo-icon-fonts-with-react-native-and.html
This question is old, but what worked for me and quite straightforward is
import { Ionicons } from "#expo/vector-icons";
await Font.loadAsync({...Ionicons.font, ...other imports })
Check if you have any dependency warnings when you run the app. I had an expo-font version warning, when I fixed it this error went away.
Some dependencies are incompatible with the installed expo package version:
- expo-font - expected version range: ~8.4.0 - actual version installed: ^9.1.0