The current Expo ErrorRecovery docs do not provide any example using ErrorRecovery.setRecoveryProps().
If you know how to use it the Managed workflow, so that it catches JS errors, let me know.
Thank you
I gave it a try:
import * as React from "react";
import { Text, View, StyleSheet } from "react-native";
import Constants from "expo-constants";
import { Button } from "react-native-paper";
import * as ErrorRecovery from "expo-error-recovery";
export default function App({ exp }) {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>{JSON.stringify(exp.errorRecovery)}</Text>
<Button
onPress={() => {
ErrorRecovery.setRecoveryProps({ info: "User pressed crash button" });
throw "Crash!";
}}
>
Crash!
</Button>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
paddingTop: Constants.statusBarHeight,
backgroundColor: "#ecf0f1",
padding: 8
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: "bold",
textAlign: "center"
}
});
When you load the app initially, exp.errorRecovery will be null. If you click the "Crash!" button and reload then exp.errorRecovery will contain whatever you set with setRecoveryProps().
I think the idea is that you can use setRecoveryProps() to let your app know that it crashed and maybe provide some context about what happened just before the crash. I’ve never used it, though, so I’m not sure what a good use case is.
EDIT:
The above might not be all that useful, but you can combine it with the approach shown here (using a global error handler) to pass some information about the crash to the reloaded app:
import * as React from "react";
import { Text, View, StyleSheet } from "react-native";
import Constants from "expo-constants";
import { Button } from "react-native-paper";
import * as ErrorRecovery from "expo-error-recovery";
const defaultErrorHandler = ErrorUtils.getGlobalHandler();
const globalErrorHandler = (err, isFatal) => {
console.log("globalErrorHandler called!");
ErrorRecovery.setRecoveryProps({ info: err });
defaultErrorHandler(err, isFatal);
};
ErrorUtils.setGlobalHandler(globalErrorHandler);
export default function App({ exp }) {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>{JSON.stringify(exp.errorRecovery)}</Text>
<Button
onPress={() => {
throw "Crash!";
}}
>
Crash!
</Button>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
paddingTop: Constants.statusBarHeight,
backgroundColor: "#ecf0f1",
padding: 8
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: "bold",
textAlign: "center"
}
});
for what it's worth, this is from the expo ErrorRecovery source code, so its essentially doing the same thing as the link you posted in your edit
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import { Platform } from 'react-native';
import { getRecoveryPropsToSave } from './ErroRecoveryStore';
import ExpoErrorRecovery from './ExpoErrorRecovery';
if (Platform.OS !== 'web') {
const globalHandler = ErrorUtils.getGlobalHandler();
// ErrorUtils came from react-native
// https://github.com/facebook/react-native/blob/1151c096dab17e5d9a6ac05b61aacecd4305f3db/Libraries/vendor/core/ErrorUtils.js#L25
ErrorUtils.setGlobalHandler(async (error, isFatal) => {
if (ExpoErrorRecovery.saveRecoveryProps) {
await ExpoErrorRecovery.saveRecoveryProps(getRecoveryPropsToSave());
}
globalHandler(error, isFatal);
});
} else if (Platform.OS === 'web' && canUseDOM) {
window.addEventListener('error', () => {
ExpoErrorRecovery.saveRecoveryProps(getRecoveryPropsToSave());
});
}
Related
My app is not loading after splash screen. It just stuck there.
So I added expo-splash-screen and still is not passing the splash screen. Before adding splash screen everything was working fine :(
See this is my App.js code. As you can see it only holds the navigation container which holds the links to other screens including the main home screen.
import {StatusBar } from 'expo-status-bar';
import { StyleSheet } from 'react-native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { MaterialIcons } from "#expo/vector-icons";
import { HomeNavigator } from './CustomNavigation';
import * as Font from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import FavouritesScreen from './src/screens/FavouritesScreen'
import HomeScreen from './src/screens/HomeScreen';
import MoreOptions from './src/screens/MoreOptions'
import React, { useEffect, useState } from 'react'
console.reportErrorsAsExceptions = false; //to hide touch warning
const Stack = createNativeStackNavigator()
const Tab = createBottomTabNavigator();
SplashScreen.preventAutoHideAsync();
export default function App() {
const [fontLoaded, setFontLoaded] = useState(false)
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
await Font.loadAsync(Entypo.font);
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (e) {
console.warn(e);
} finally {
// Tell the application to render
setAppIsReady(true);
}
}
prepare();
}, []);
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;` `
}
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}
onLayout={onLayoutRootView}>
<StatusBar barStyle="dark-content" hidden={false} backgroundColor="#ff3b3b" translucent={true} />
<NavigationContainer>
//it contains nothing but the navigation code
</NavigationContainer>
</View>)
Please tell me where and what I'm doing wrong here.
I give you the working example please check it
then after you don't understand let me know
code
import React, { useRef, useEffect } from 'react';
import { StyleSheet, View, Image, StatusBar, Text } from 'react-native';
/**
* RN libraries
*/
import { useNavigation } from '#react-navigation/native';
export default function Splash() {
const animation = useRef(null);
const navigation = useNavigation();
useEffect(() => {
animation.current?.play();
navigation.addListener('focus', () => {
animation.current?.play();
});
setTimeout(() => {
navigate();
}, 2500);
}, []);
const navigate = () => {
navigation.navigate('Home');
};
return (
<View style={styles.animationContainer}>
<Text
style={{
textAlign: 'center',
fontSize: 50,
fontStyle: 'italic',
fontFamily: 'Poppins-Medium',
fontWeight: '800',
color: '#fff',
}}>
Online{'\n'}Education
</Text>
<Text
style={{ color: '#fff', fontSize: 20, fontFamily: 'Poppins-Medium' }}>
Free{'\n'}
</Text>
<View
style={{
backgroundColor: '#5D6CFA',
width: 169,
height: 117,
borderRadius: 60,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
}}>
{/* <Text style={{color:"#000",fontFamily:'Poppins-Bold',fontSize:25}}>Let's start</Text> */}
</View>
</View>
);
}
const styles = StyleSheet.create({
animationContainer: {
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'center',
flex: 1,
},
});
Maybe pass your setAppIsReady(true); after the finally and then remove it like this
React.useEffect(() => {
async function prepare() {
try {
// Pre-load fonts, make any API calls you need to do here
AsyncStorage.setItem("alreadyAppLaunched", "true")
await LoadFonts()
await checkOsDarkMode()
await stores.AuthStore.getAllData()
} catch (e) {
console.warn(e);
}
setReady(true);
}
prepare()
}, [])
I am using Expo to build a react native app with AWS for the backend.
I am trying to display a list of friends using FlatList and the AWS data. The list works and is visible on my web browser, but for some reason, it is not displaying on my Android phone. What might the issue be?
FriendsList.tsx
import { API, graphqlOperation } from 'aws-amplify';
import * as React from 'react';
import {useEffect, useState} from 'react';
import { FlatList, View, ScrollView, Text, StyleSheet } from 'react-native';
import { listUsers } from '../graphql/queries';
import FriendsListItem from '../components/FriendsListItem';
export default function FriendsList() {
const [ users, setUsers ] = useState([]);
useEffect( () => {
const fetchUsers = async () => {
try {
const usersData = await API.graphql(
graphqlOperation(
listUsers
)
)
setUsers(usersData.data.listUsers.items);
} catch (e) {
}
}
fetchUsers();
},[])
return (
<View style={ styles.container }>
<FlatList
style={{ width: '100%' }}
data={users}
renderItem={({ item }) => <FriendsListItem user={item} />}
keyExtractor={(item) => item.id}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
FriendsListItem.tsx
import * as React from 'react';
import { View, Text, StyleSheet, Image, TouchableWithoutFeedback } from 'react-native';
import { API, graphqlOperation, Auth } from "aws-amplify";
import { User } from './types';
export type FriendsListItemProps = {
user: User;
}
const FriendsListItem = ( props: FriendsListItemProps ) => {
const { user } = props;
return (
<TouchableWithoutFeedback>
<View style={styles.container}>
<View style={styles.lefContainer}>
<Image source={{ uri: user.imageUri }} style={styles.avatar}/>
<View style={styles.midContainer}>
<Text style={styles.username}>{user.name}</Text>
<Text numberOfLines={2} style={styles.status}>{user.email}</Text>
</View>
</View>
</View>
</TouchableWithoutFeedback>
);
}
export default FriendsListItem;
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
width: "100%",
justifyContent: 'space-between',
//height: '100%',
},
lefContainer: {
flexDirection: 'row',
padding: 16,
},
midContainer: {
justifyContent: 'space-around'
},
avatar: {
width: 60,
height: 60,
borderRadius: 50,
marginRight: 15,
},
username: {
fontWeight: 'bold',
fontSize: 16,
},
status: {
fontSize: 16,
color: 'grey',
},
});
Looking at the code example, in your FriendsListItem component, any time you use your "user" prop, you need to change it. For example, change this:
user.imageUri
to this:
user.item.imageUri
What you are passing in is an object (e.g. user), which then contains another object (e.g. item), which finally contains your data (e.g. imageUri).
I figured it out. Turns out I am just an idiot. The expo app on my phone was logged into a different account so that's why it wasn't showing any friends for that user.
When running the code below in react-native, I get a "buttonColor is read-only" error.
I've been reviewing documentation from various places on useState but am still not sure what is causing this particular error.
I would expect for the button to alternate between 'green' (the initial state) and 'red' after each subsequent tap, but it just show green on the first render and then I get the error message after the first tap.
import React, {useState} from 'react';
import {StyleSheet, Text, View, TouchableOpacity} from 'react-native';
const ColorSquare = () => {
const[buttonColor, setColor] = useState('green');
const changeColor = () => {
if (buttonColor='green') {
setColor('red')
} else if (buttonColor='red') {
setColor('green')
} else {
setColor('blue')
}
}
return (
<View style={styles.container}>
<TouchableOpacity
style={{backgroundColor:buttonColor, padding: 15}}
onPress={()=>changeColor()}
>
<Text style={styles.text}>Change Color!</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
text:{
color:'white'
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
});
export default ColorSquare;
Problem is you are assigning the value of button color instead of comparing it i.e using = instead of ===. You are basically setting a value to button color which is wrong.
import React, {useState} from 'react';
import {StyleSheet, Text, View, TouchableOpacity} from 'react-native';
const ColorSquare = () => {
const[buttonColor, setColor] = useState('green');
const changeColor = () => {
if (buttonColor==='green') { // use === instead of == //
setColor('red')
} else if (buttonColor==='red') { // use === instead of == //
setColor('green')
} else {
setColor('blue')
}
}
return (
<View style={styles.container}>
<TouchableOpacity
style={{backgroundColor:buttonColor, padding: 15}}
onPress={()=>changeColor()}
>
<Text style={styles.text}>Change Color!</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
text:{
color:'white'
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
});
export default ColorSquare;
In addition to the answer posted by Atin above, I also thought of a way to do this using numerical values and an array of colors which I ultimately chose to go with due to needing some underlying binary representation of the data.
import React, {useState} from 'react';
import {StyleSheet, Text, View, TouchableOpacity} from 'react-native';
const ColorSquare = () => {
const[buttonNumber, setNumber] = useState(0);
const colors = ['green','red']
const changeColor = () => {
if (buttonNumber<1) {
setNumber(buttonNumber => buttonNumber + 1)
} else {
setNumber(buttonNumber => buttonNumber - 1)
}
}
return (
<View style={styles.container}>
<TouchableOpacity
style={{backgroundColor:colors[buttonNumber], padding: 15}}
onPress={()=>changeColor()}
>
<Text style={styles.text}>Change Color!</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
text:{
color:'white'
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
});
export default ColorSquare;
HI there if you are in 2021 having this problem I want to show you something that I was doing wrong
In my case I was trying to use a handleChange function to setState look here below
const handleChange = (e) => {
setCustomerLogin({...customerLogin, [e.target.name]: e.target.value})
I was getting a setCustomerLogin is ready only and its because I wrapped the (e) in the above example.
Solution is
const handleChange = e => {
setCustomerLogin({...customerLogin, [e.target.name]: e.target.value})
I have just started integrating Redux into my first React Native (Expo.io) project. I have got login working great with Redux, but when I try and create a logout button on one of the screens in my app, it actually triggers the log out dispatch as soon as it loads it. I think I must be misunderstanding the way Redux connect and mapDispatchToProps work. I have read the documentation many times but am still stuck. Here is the code in it's non-working state.
Log In - Works until I add the log out dispatch on profile page
import { connect } from "react-redux";
import React, { Component } from "react";
import {
Button,
View,
Text,
ActivityIndicator,
Alert,
FlatList
} from "react-native";
import { NavigationActions } from "react-navigation";
import { SocialIcon, Card } from "react-native-elements";
import Reactotron from "reactotron-react-native";
import { logIn } from "../actions";
import { signIn } from "../components/Auth";
class SignIn extends Component {
async handleClick() {
res = await signIn();
if (res != false) {
this.props.logIn(res.token);
} else {
console.log("Login Failed");
}
}
render() {
return (
<View style={{ paddingVertical: 20 }}>
<Card title="finis Requires A Facebook Account To Operate">
<SocialIcon
title="Fred"
button
type="facebook"
onPress={() => this.handleClick()}
/>
</Card>
</View>
);
}
}
const mapDispatchToProps = dispatch => {
return {
logIn: fbToken => {
dispatch(logIn(fbToken));
}
};
};
LoginScreen = connect(null, mapDispatchToProps)(SignIn);
export default LoginScreen;
Reducers
import { combineReducers } from "redux";
import Reactotron from "reactotron-react-native";
import { LOG_IN, LOG_OUT, ADD_PHONE_CONTACTS } from "../actions/actions";
const initialState = {
signedIn: false,
fbToken: "fred",
test: undefined,
phoneContacts: {}
};
const finis = combineReducers({
auth,
phoneContacts
});
function auth(state = initialState, action) {
switch (action.type) {
case LOG_IN:
Reactotron.log("LOG IN");
return {
...state,
signedIn: true,
fbToken: action.fbToken
};
case LOG_OUT:
Reactotron.log("LOG OUT");
return {
...state,
signedIn: false,
fbToken: undefined
};
default:
return state;
}
}
function phoneContacts(state = [], action) {
switch (action.type) {
case ADD_PHONE_CONTACTS:
console.log("Adding Contacts");
return {
...state,
phoneContacts: action.phoneContacts
};
default:
return state;
}
}
export default finis;
Profile Non working. Triggers LOG_OUT action without the button being pushed.
import React, { Component } from "react";
import { Button, Card } from "react-native-elements";
import { View, Text, ActivityIndicator, AsyncStorage } from "react-native";
import { MapView } from "expo";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import { SimpleLineIcons } from "#expo/vector-icons";
import Reactotron from "reactotron-react-native";
import * as ActionCreators from "../actions";
import { signOut } from "../components/Auth";
class ProfileWrap extends Component {
handleClick() {
Reactotron.log(this.Actions);
this.props.logOut();
}
render() {
return (
<View style={{ paddingVertical: 20 }}>
<Card title="Profile">
<View
style={{
backgroundColor: "#bcbec1",
alignItems: "center",
justifyContent: "center",
width: 80,
height: 80,
borderRadius: 40,
alignSelf: "center",
marginBottom: 20
}}
>
<Text style={{ color: "white", fontSize: 28 }}>JD</Text>
</View>
<Button title="Log Out" onPress={this.handleClick} />
</Card>
</View>
);
}
}
mapDispatchToProps = dispatch => {
return {
logOut: dispatch(logOut())
};
};
const Profile = connect(null, mapDispatchToProps)(ProfileWrap);
export default Profile;
Any help would be appreciated, even if it's telling me I'm doing the whole thing wrong :) Been at this for hours.
NEW Profile.js - Gives Cannot read property 'logOut' of undefined
import React, { Component } from "react";
import { Button, Card } from "react-native-elements";
import { View, Text, ActivityIndicator, AsyncStorage } from "react-native";
import { MapView } from "expo";
import { connect } from "react-redux";
import { SimpleLineIcons } from "#expo/vector-icons";
import { logOut } from "../actions";
import { signOut } from "../components/Auth";
class ProfileWrap extends Component {
handleClick() {
console.log(this.props);
this.props.logOut();
}
render() {
return (
<View style={{ paddingVertical: 20 }}>
<Card title="Profile">
<View
style={{
backgroundColor: "#bcbec1",
alignItems: "center",
justifyContent: "center",
width: 80,
height: 80,
borderRadius: 40,
alignSelf: "center",
marginBottom: 20
}}
>
<Text style={{ color: "white", fontSize: 28 }}>JD</Text>
</View>
<Button title="Log Out" onPress={this.handleClick} />
</Card>
</View>
);
}
}
const mapDispatchToProps = dispatch => {
return {
logOut: function() {
dispatch(logOut());
}
};
};
const Profile = connect(null, mapDispatchToProps)(ProfileWrap);
export default Profile;
Your mapDispatchToProps should be returning an object with functions. The way you have it now, logOut() will be called immediately since it's not inside a function. Changing it to this should fix it:
const mapDispatchToProps = dispatch => {
return {
logOut: function () {
dispatch(logOut());
}
};
};
Here's a slightly cleaner way of doing it:
const mapDispatchToProps = dispatch => ({
logOut() {
dispatch(logOut());
}
});
Also, you're missing const in front of mapDispatchToProps but that shouldn't have affected anything.
Edit:
You don't have to use this now, but it'll be helpful in the future - if your component is only using the render method you can change it to a stateless functional component. It's currently the recommended way of creating components when possible:
const ProfileWrap = props => (
<View style={{ paddingVertical: 20 }}>
<Card title="Profile">
<View
style={{
backgroundColor: "#bcbec1",
alignItems: "center",
justifyContent: "center",
width: 80,
height: 80,
borderRadius: 40,
alignSelf: "center",
marginBottom: 20
}}
>
<Text style={{ color: "white", fontSize: 28 }}>JD</Text>
</View>
<Button title="Log Out" onPress={props.logOut} />
</Card>
</View>
);
Similar to Focus style for TextInput in react-native, I am trying to change the property underlineColorAndroid for the textInput.
I am using React-Native 0.28.0
OnFocus, the attribute doesn't change. How do I change the underline to a different color onFocus?
My sample code is below:
'use strict';
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TextInput,
ScrollView
} from 'react-native';
class RNPlayground extends Component {
constructor(props) {
super(props);
this.state = {
hasFocus: false
}
}
_onBlur() {
this.setState({hasFocus: false});
}
_onFocus() {
this.setState({hasFocus: true});
}
_getULColor(hasFocus) {
console.error(hasFocus);
return (hasFocus === true) ? 'pink' : 'violet';
}
render() {
console.error("this.state=");
console.error(this.state);
console.error("this.state.hasFocus=");
console.error(this.state.hasFocus);
return (
<View style={styles.container}>
<ScrollView>
<TextInput
placeholder="textInput"
onBlur={ () => this._onBlur() }
onFocus={ () => this._onFocus() }
style={styles.instructions}
underlineColorAndroid={this._getULColor(this.state.hasFocus)}/>
</ScrollView>
<TextInput>Some sample text</TextInput>
</View>
);
}
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 28,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
fontSize: 19,
marginBottom: 5,
}
});
AppRegistry.registerComponent('RNPlayground', () => RNPlayground);
Well, your code is correct and it should work properly.
Here is working example
Please, stop downvote this answer. Unfortunately rnplay service isn’t available anymore, as like this example. Or downvote but explain your point.