Why does my Expo App crash even with an ErrorBoundary? - react-native

I added an ErrorBoundary to my app, but even with an ErrorBoundary my app crashes on errors. Here is a snack with some sample code: https://snack.expo.dev/jq6vGCOoP, also included below:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null, hasError: false, errorInfo: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// Catch errors in any components below and re-render with error message
this.setState({
error,
hasError: true,
errorInfo,
});
// You can also log error messages to an error reporting service here
}
render() {
if (this.state.hasError) {
// Error path
return (
<View>
<Text>Something went wrong.</Text>
</View>
);
}
// Normally, just render children
return this.props.children;
}
}
export default function App() {
return (
<ErrorBoundary>
<AppInner/>
</ErrorBoundary>
)
}
function AppInner() {
throw new Error('Error here');
return <></>;
}
I've defined my ErrorBoundary and wrapped it around AppInner, a child component in which I manually throw an error. It kind of works, in developer mode when I run my app using expo start, I get a red error which I can dismiss and I can see the Something went wrong error text. However, when I run the app in production mode using expo start --no-dev --minify, the app just crashes. This is also expected if it crashes locally, but I've also tried pushing the app up onto Expo using eas update, and the app also crashes when trying to load it through Expo hosted services.
Anyone have any ideas?

Related

TypeError: undefined is not an object (evaluating 'ExponentSQLite.exec')

ive initiated a new react-native app using npx init where i transferred a code i was using on anothe react native app. this code uses react expo-sqlite. i installed expo-sqlite but when i run the app, i get this annoying error:
TypeError: undefined is not an object (evaluating 'ExponentSQLite.exec')
ive tried to update expo to latest version but the error still persists. i even deleted node-modules and re-installed using npm istall but it isnt working. how do i fix it? I'm running bare react-native which also uses some expo packages.
this is my code:
import React from "react";
import { Text } from "react-native";
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("silkyMarket.db");
export default class SQLiteScreen extends React.Component {
constructor() {
super();
this.state = {
todo: [],
search: "",
};
}
componentDidMount() {
// create user table if not exist
db.transaction((tx) => {
tx.executeSql(
"create table if not exists tbl_register (id integer primary key not null, name text, contact text);"
);
});
// check if registered
const query = "select *from tbl_register";
const array = [];
db.transaction((tx) => {
tx.executeSql(query, array, (tx, results) => {
this.setState({ todo: results.rows });
console.log(this.state.todo._array);
});
});
}
render() {
return <Text>this is important</Text>;
}
}
If you are using the Expo bare workflow, you must delete the Android folder and run expo run:android again.
After these steps, the mentioned error stopped for me.

React native UI is not getting rendered after callback from native event emitter. Even callback having state change

I want to navigate the user to another screen in react native project after native app widget click in android. I was able to catch event using native event emitter in my MainView.js and there i changed state of one of my component and it got changed but UI is not getting rendered after this state change. It is showing blank screen and there is not error on the console. Thanks in advance for any help!!
export default class MainView extends React.Component {
constructor(props) {
super(props);
this.state = {text: 'Hi, This is main screen for app widget!!!'};
}
componentDidMount() {
const eventEmitter = new NativeEventEmitter();
this.listener = eventEmitter.addListener('MyCustomEvent', (event) => {
console.log('MyCustomEvent -->', event);
console.log('MyCustomEvent ArticleId -->', event.ArticleId);
if (event.ArticleId === data.articleId) {
console.log('data ArticleId true', data.articleId);
//navigation.push('Article Details', data);
this.setState({
text: data.articleDes,
});
// setText(data.articleDes);
console.log('text -->', this.state.text);
} else {
// setText('No such article found.');
console.log('text -->', this.state.text);
}
});
}
componentWillUnmount() {
this.eventListener.remove(); //Removes the listener
}
render() {
return (
<View style={{flex: 1}}>
<Text>{this.state.text}</Text>
<Button
title="click"
onPress={() => this.props.navigation.push('Article Details', data)}
/>
</View>
);
}
}
CustomActivity source code which is launched from appwidget click. From this activity's oncreate, I'm emitting events to react-native main view.
int articleId = 0;
if (getIntent() != null) {
articleId = getIntent().getIntExtra("articleId", 0);
Log.e("articleid", "" + articleId);
}
// Put data to map
WritableMap payload = Arguments.createMap();
payload.putInt("ArticleId", articleId);
// Emitting event from java code
ReactContext context = getReactNativeHost().getReactInstanceManager().getCurrentReactContext();
if ( context != null && context.hasActiveCatalystInstance()) {
Log.e("react context", "not null");
(getReactNativeHost().getReactInstanceManager().getCurrentReactContext())
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("MyCustomEvent", payload);
}
That is not how to use NativeEventEmitter. You need to initialise the NativeEventEmitter with the native module you are emitting events from:
import { NativeEventEmitter, NativeModules } from 'react-native';
const { myNativeModule } = NativeModules;
componentDidMount() {
...
const eventEmitter = new NativeEventEmitter(myNativeModule);
this.eventListener = eventEmitter.addListener('myEvent', (event) => {
console.log(event.eventProperty) // "someValue"
});
...
}
componentWillUnmount() {
this.eventListener.remove(); //Removes the listener
}
Read more about NativeModules here: https://reactnative.dev/docs/native-modules-android
This sound familiar with an issue I am experiencing on IOS. The code is similar, but I cannot guarantee that the underlying structure in Android works in the same way. Anyways, I am sending an event message from IOS-Native (written in swift in xCode) to React-native file using the NativeEventEmitter. After the initial render, the value just wont update, and as I understand this issue is not limited to this type of Event. After some googling I found out that everything you read from state inside that event-callback has a reference to only the first render, and will not update on future renders.
Solution; use useRef so you keep a reference to the the updated value. useRef keeps the value across renders and event-callbacks. This is not something I have found out myself, please look at https://medium.com/geographit/accessing-react-state-in-event-listeners-with-usestate-and-useref-hooks-8cceee73c559 and React useState hook event handler using initial state for, they are the one that deserves the credit.

class constructors must be invoked with 'new' error in React-native

So I'm trying to use react native and aws-amplify to build a web and mobile app with one code base. Now I just added the withAuthenticator component to my app which works fine on the mobile part but when i run the web app in the browser I'm unable to type into the authentication field that is if I click on it the cursor appears and emmediately dissapears suprisingly though if i click and hold I'm suddenly able to type aslong as I'm holding the mouse button. So after that I tried to instead create my custom authenticator which worked fine for mobile but on the web it returned the error class constructors must be invoked with 'new'. My code is below hope someone can help me with this. Thanks!
class MySignIn extends SignIn {
constructor(props) {
super(props);
this.state = {
username: null,
password: null,
error: null
}
this.checkContact = this.checkContact.bind(this);
this.signIn = this.signIn.bind(this);
}
render() {
if (this.props.authState !== 'signIn') {
return null;
}
return(
<View style={{flex:1, backgroundColor: 'blue'}} behavior="padding">
<Text>Stuff and Stuff</Text>
</View>
);
}
}
export default withAuthenticator(Signout, false, [
<MySignIn/>,
<ConfirmSignIn/>,
<VerifyContact/>,
<SignUp/>,
<ConfirmSignUp/>,
<ForgotPassword/>,
<RequireNewPassword />
]);
class MySignIn extends SignIn is this correct? ive usually imported class of class MySignIn extends React.Component
and that works because they have imported signIn from
import { ConfirmSignIn, ConfirmSignUp, ForgotPassword, RequireNewPassword, SignIn, SignUp, VerifyContact, withAuthenticator } from 'aws-amplify-react';
and hence this is a custom class component , hence it works
// This is my custom Sign in component
class MySignIn extends SignIn {
render() {
...
}
}
this works. Hope it helps

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')

Simple example for accessing the camera via WebRTC in react-native (Android)

I'm trying to adapt an augmented reality app I wrote in JS that only works in Firefox on Android to a react native app that can work in either Android or iOS. Since I need the camera input, I'm using react-native-webrtc (rather than importing the html and js I have been using, since I'm also trying to reduce framerate lag). I've been trying to parse out the demo here:
https://github.com/oney/RCTWebRTCDemo/blob/master/main.js
But the demo app is quite complex since it is a video chatroom (from what I can surmise). I just need to access the camera and keep it as the background of the app. This is what I have so far:
import React, { Component } from 'react';
import {
AppRegistry,
View,
} from 'react-native';
import {
RTCPeerConnection,
RTCMediaStream,
RTCIceCandidate,
RTCSessionDescription,
RTCView,
MediaStreamTrack,
getUserMedia,
} from 'react-native-webrtc';
let localStream;
function getLocalStream(isFront, callback) {
MediaStreamTrack.getSources(sourceInfos => {
let videoSourceId;
for (const i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if(sourceInfo.kind == "video" && sourceInfo.facing == (isFront ? "front" : "back")) {
videoSourceId = sourceInfo.id;
}
}
getUserMedia({
audio: false,
video: {
mandatory: {
minWidth: 500,
minHeight: 300,
minFrameRate: 30
},
facingMode: (isFront ? "user" : "environment"),
optional: [{ sourceId: sourceInfos.id}]
}
}, function(stream) {
console.log("dddd", stream);
callback(stream);
}, logError);
});
}
function logError(error) {
console.log("logError: ", error);
}
let container;
var CanvasTest = React.createClass({
getInitialState: function() {
return {
isFront: true,
selfViewSrc: null};
},
componentDidMount: function() {
container = this;
},
render() {
return (
<View>
<RTCView streamURL={this.state.selfViewSrc} />
{console.log("this.state: ", this.state)}
{getLocalStream(true, function(stream) {
//localStream = stream;
//container.setState({selfViewSrc: stream.toURL()});
})
}
</View>
);
}
});
AppRegistry.registerComponent('CanvasTest', () => CanvasTest);
Everything is okay until I try to call the getLocalStream function. I get an "undefined is not an object" error for that line. (I've commented out the lines inside the callback to see if they are causing the problem, they are not).
This is what I get from the console in Android Studio:
E/ReactNativeJS: undefined is not an object (evaluating 'WebRTCModule.mediaStreamTrackGetSources')
E/EGL_emulation: tid 3106: eglSurfaceAttrib(1165): error 0x3009 (EGL_BAD_MATCH)
W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xa0899300, error=EGL_BAD_MATCH
I think I'm calling the function in the wrong place. I want the view to load up the camera stream when the app starts. What am I doing wrong?
Is there a simpler example somewhere of using WebRTC in react native?
About undefined is not an object
It may because of not install it properly.
I'd suggest restart a fresh build again:
remove npm module: rm -rf $YourProject/node_modules/react-native-webrtc
clean npm cache: npm cache clean
clear gradle build intermediate files or
clear xocde project by Product -> clean
( it depends on your env )
npm install react-native-webrtc
follow the documents steps by steps carefully (Android / iOS)
be sure grant all permissions mentions on documents then try again.
Where to execute getLocalStream()
in your case, you can execute it in ComponentDidMount
otherwise, in some case, app may warn that you can't setState() in render()
(setState() will trigger render() normally, the warning is to prevent infinite loop.)
Suggestion
I would suggest you to NOT test it on simulators as possible for libraries which needs to access lots of hardware's functionalities.