expo AppLoading startAsync Deprecated. what is the alternative? - react-native

I am learning react native from Udemy. In one of the lessons I saw AppLoading has been used for loading fonts.
So i want to learn about it in documentation from here. I am able to use that without any issues even though, I saw here that startAsync has been deprecated.
What is the alternative to this startAsync if it stopped working?
below is the code from documentation,
import React from 'react';
import { Image, Text, View } from 'react-native';
import { Asset } from 'expo-asset';
import AppLoading from 'expo-app-loading';
export default class App extends React.Component {
state = {
isReady: false,
};
render() {
if (!this.state.isReady) {
return (
<AppLoading
startAsync={this._cacheResourcesAsync}
onFinish={() => this.setState({ isReady: true })}
onError={console.warn}
/>
); }
return (
<View style={{ flex: 1 }}>
<Image source={require('./assets/snack-icon.png')} />
</View>
);
}
async _cacheResourcesAsync() {
const images = [require('./assets/snack-icon.png')];
const cacheImages = images.map(image => {
return Asset.fromModule(image).downloadAsync();
});
return Promise.all(cacheImages);
}
}

Call _cacheResourcesAsync function in componentDidMount and when all promised are resolved set state isReady to true like:
import React from 'react';
import { Image, Text, View } from 'react-native';
import { Asset } from 'expo-asset';
import AppLoading from 'expo-app-loading';
export default class App extends React.Component {
state = {
isReady: false,
};
componentDidMount(){
_cacheResourcesAsync();
}
render() {
if (!this.state.isReady) {
return (
<AppLoading />
); }
return (
<View style={{ flex: 1 }}>
<Image source={require('./assets/snack-icon.png')} />
</View>
);
}
_cacheResourcesAsync() {
const images = [require('./assets/snack-icon.png')];
const cacheImages = images.map(image => {
return Asset.fromModule(image).downloadAsync();
});
Promise.all(cacheImages).then(()=>{
this.setState({ isReady : true });
});
}
}

You should use a hook to load your images.
First, create a hook to load resources in a separate file:
import * as SplashScreen from 'expo-splash-screen';
import { useEffect, useState } from 'react';
export default function useCachedResources() {
const [isLoadingComplete, setLoadingComplete] = useState(false);
// Load any resources or data that we need prior to rendering the app
useEffect(() => {
async function loadResourcesAndDataAsync() {
try {
SplashScreen.preventAutoHideAsync();
// Load images
const images = [require('./assets/snack-icon.png')];
await images.map(async image =>
await Asset.fromModule(image).downloadAsync());
} catch (e) {
// We might want to provide this error information to an error reporting service
console.warn(e);
} finally {
setLoadingComplete(true);
SplashScreen.hideAsync();
}
}
loadResourcesAndDataAsync();
}, []);
return isLoadingComplete;
}
Then call it in your App component - I changed it to a function component, because hooks don't work in classes and it is now the recommended way of coding react:
import { Image, View } from 'react-native';
import useCachedResources from "./hooks/useCachedResources";
export default function App() {
const isLoadingComplete = useCachedResources();
if (!isLoadingComplete) {
return null;
}
return (
<View style={{ flex: 1 }}>
<Image source={require('./assets/snack-icon.png')} />
</View>
);
}

Related

expo camera issue once it work second time without refresh it can not work

I'm using an expo camera for QRcode scan with react-navigation. it works fine only at first after that use of second time the screen of the camera became black. It can not work. but with refresh the app it work fine once
can any suggest me the best solution for it?
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import { Camera } from 'expo-camera';
import {SetHeight,SetWidth}from '../utillies/Display'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import Entypo from 'react-native-vector-icons/Entypo'
import { withNavigationFocus } from 'react-navigation'
const QrcodeReader = ({navigation,isFocused}) => {
const [hasPermission, setHasPermission] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
useEffect(() => {
(async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return(
<View style={styles.container}>
{isFocused&& <Camera
type={type}
onBarCodeScanned={(...args) => {
const data = args[0].data;
const result = JSON.stringify(data);
navigation.navigate('ItemCollector',{FKUserId:result});
}}
barCodeScannerSettings={{
barCodeTypes: ['qr'],
}}
style={{ flex: 1 }}
>
</Camera>}
</View>
);
}
export default withNavigationFocus(QrcodeReader);

Login component react-native

I am making a react-native application.
I am trying to find out how can I show or hide the login component on the basis of a token stored. If the token is present in the storage, it would skip the login screen to the dashboard.
Else it will show login screen.
Here is the code to the app.
import React, { useState } from 'react';
import { StyleSheet, Text, View, Button, AsyncStorage } from 'react-native';
import Login from './components/Login';
import Dashboard from './components/Dashboard';
export default function App() {
var [ token, setToken ] = useState('');
function loginHandler(recievedToken) {
setToken(recievedToken);
}
async function _readData() {
try {
const value = await AsyncStorage.getItem('Token');
console.log(value);
if (value !== null) {
// We have data!!
loginHandler(value)
} else {
loginHandler(null);
}
} catch (error) {
// Error retrieving data
}
};
return (
<View>
<Login getToken={_readData()} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
You can put conditional statement in your code.
Try this code out.
import React, { useState } from 'react';
import { StyleSheet, Text, View, Button, AsyncStorage, ActivityIndictor } from 'react-native';
import Login from './components/Login';
import Dashboard from './components/Dashboard';
export default function App() {
var [ token, setToken ] = useState('');
var [ loading, setLoading ] = useState(true);
function loginHandler(recievedToken) {
setToken(recievedToken);
setLoading(false);
}
async function _readData() {
try {
const value = await AsyncStorage.getItem('Token');
console.log(value);
if (value !== null) {
// We have data!!
loginHandler(value)
} else {
loginHandler(null);
}
} catch (error) {
// Error retrieving data
}
};
if( loading ) {
<ActivityIndictor/>
}
if(token == null){
return (
<View>
<Login getToken={_readData()} />
</View>
);
} else {
return (
<View>
<Dashboard />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Let me know if some error or correction.
Here AuthStack is Login Screen and BottomNavigation is DashBoard.
import React, { useEffect, useState } from "react";
import { NavigationContainer } from "#react-navigation/native";
import AuthStackScreen from "./routes/AuthStack";
import BottomNavigation from "./routes/BottomNavigator";
import firebase from "./config/firebase";
export default App = () => {
const [isLogin, setIsLogin] = useState(false);
const checkLoginIn = () => {
firebase.auth().onAuthStateChanged(user => {
if (user) {
setIsLogin(true);
} else {
setIsLogin(false);
}
});
};
useEffect(() => {
checkLoginIn();
}, []);
return (
<NavigationContainer>
{isLogin ? <BottomNavigation /> : <AuthStackScreen />}
</NavigationContainer>
);
};
In my case
I just used initialRouteName
import React, {Component} from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createNativeStackNavigator} from '#react-navigation/native-stack';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Home from './components/Home';
import Login from './components/Login';
const Stack = createNativeStackNavigator();
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
routeName: 'Login',
};
}
async componentDidMount() {
const token = JSON.parse(await AsyncStorage.getItem('token'));
if (token !== null) {
this.setState({
routeName: 'Bluetooth',
});
}
SplashScreen.hide();
}
render() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName={this.state.routeName}
screenOptions={{
headerShown: false,
}}>
<>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Home" component={Home} />
</>
</Stack.Navigator>
</NavigationContainer>
);
}
}

apploading splashscreen with hooks

How do you achieve loading resources while showing the splash screen when you are using functional components with hooks? What is the pattern in using apploading and/or splashscreen with hooks?
Thanks!
Bill
If you only understand Hook's useState, this is a very easy change. This is simply converted into a function, and the state value is resolved using hooks. If you change the example of AppLoading to Hook, the code below is as follows.
AppLoading use Hooks
import React, { useState } from 'react';
import { View ,Image } from "react-native";
import { Asset } from 'expo-asset';
import { AppLoading } from 'expo';
export default function App() {
const [isReady, setReady] = useState(false);
const _cacheResourcesAsync = async () => {
const images = [require('./assets/snack-icon.png')];
const cacheImages = images.map(image => {
return Asset.fromModule(image).downloadAsync();
});
return Promise.all(cacheImages);
}
return (
isReady === false ? ( <AppLoading
startAsync={_cacheResourcesAsync}
onFinish={() => setReady(true)}
onError={console.warn}
/>) : (<View style={{ flex: 1 }}>
<Image source={require('./assets/snack-icon.png')} />
</View>)
);
}

Expo AppLoading and app bootstrap data?

There is a new component <AppLoading/> that is supposed to let the splash screen remain visible while loading app resources.
The example in the doc is pretty straightforward
https://docs.expo.io/versions/latest/sdk/app-loading.html
import React from 'react';
import { Image, Text, View } from 'react-native';
import { Asset, AppLoading } from 'expo';
export default class App extends React.Component {
state = {
isReady: false,
};
render() {
if (!this.state.isReady) {
return (
<AppLoading
startAsync={this._cacheResourcesAsync}
onFinish={() => this.setState({ isReady: true })}
onError={console.warn}
/>
);
}
return (
<View style={{ flex: 1 }}>
<Image source={require('./assets/images/expo-icon.png')} />
<Image source={require('./assets/images/slack-icon.png')} />
</View>
);
}
async _cacheResourcesAsync() {
const images = [
require('./assets/images/expo-icon.png'),
require('./assets/images/slack-icon.png'),
];
const cacheImages = images.map((image) => {
return Asset.fromModule(image).downloadAsync();
});
return Promise.all(cacheImages)
}
}
However is this kind of component intended to handle loading resources that can fail?
Like for example if my app needs bootstrap datas regarding the authenticated users, provided by a backend, should I use this component?
If AppLoading is suited for this need, how would you handle the case where the user starts the app with no connexion, and the bootstrap data promise rejects? How do you handle retry attempts?

react native navigation Could not find "store" in either the context or props of "Connect(facebookLogin)

I'm building react native app authentication with facebook login.
I'm using stack navigator and I want to add redux to my app.
I just seperate my files to components and when I use stack navigation with redux I get this error
Could not find "store" in either the context or props of
"Connect(facebookLogin)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(facebookLogin)".
index.android.js
import React, { Component } from 'react';
import { Text, View, StyleSheet, TouchableOpacity, AppRegistry, Image } from 'react-native';
import facebookLogin from './src/components/FacebookLogin';
import Home from './src/components/Home';
import {
StackNavigator,
} from 'react-navigation';
const App = StackNavigator({
Home: { screen: facebookLogin },
HomePage: {screen:Home},
})
AppRegistry.registerComponent('facebookLogin', () => App);
FacebookLogin.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import { Text, View, StyleSheet, TouchableOpacity, AppRegistry, Image } from 'react-native';
import FBSDK, { LoginManager, GraphRequest, GraphRequestManager } from 'react-native-fbsdk';
import Icon from 'react-native-vector-icons/FontAwesome';
// redux
import { Provider } from 'react-redux';
import { connect } from 'react-redux';
import { createStore } from 'redux';
import reducers from '../reducers/ProfileReducers';
const store = createStore(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
class facebookLogin extends Component {
constructor(props) {
super(props);
this.state = {
result: null
}
}
_fbAuth() {
const { navigate } = this.props.navigation;
LoginManager.logInWithReadPermissions(['public_profile','user_birthday']).then((result) => {
if (result.isCancelled) {
console.log('Login was canclled');
}
else {
console.log(result);
const infoRequest = new GraphRequest(
'/me?fields=id,name,picture.width(800).height(800),gender,birthday,first_name,last_name',
null,
(error, result) => {
if (error) {
alert('Error fetching data: ' + error.toString());
} else {
console.log(result);
this.setState({ result });
navigate('HomePage');
}
}
);
// Start the graph request.
new GraphRequestManager().addRequest(infoRequest).start();
}
}, function (error) {
console.log('An error occured:' + error);
})
}
getProfileData() {
const { ImageStyle } = styles;
if (this.state.result) {
return (
<View>
{this.state.result.picture ? <Image style={ImageStyle} source={{ uri: this.state.result.picture.data.url }}></Image> : null}
<Text> first name: {this.state.result.first_name} </Text>
<Text> Last name: {this.state.result.last_name} </Text>
<Text> Gender {this.state.result.gender} </Text>
</View>
)
}
return null;
}
render() {
return (
<Provider store = {store}>
<View style={styles.container}>
<Icon.Button name="facebook" backgroundColor="#3b5998" onPress={() => { this._fbAuth() }}>
<Text style={{fontFamily: 'Arial', fontSize: 15, color:'white'}}>Login with Facebook</Text>
</Icon.Button>
{this.getProfileData()}
</View>
</Provider>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
ImageStyle:{
borderRadius:80,
width:100,
height:100
}
});
const mapStateToProps = (state) => {
return
{ profile: state.profile};
}
export default connect(mapStateToProps)(facebookLogin);
when it was only one component in index.android.js it works fine without any error but when I seperate it to some components using stack navigation I get this error.
my profileReducers
var profile = {
name :'',
email:'',
photo:''
}
const initialState = {
profile,
};
export default (state = initialState, action) => {
console.log("state");
switch (action.type) {
case 'INSERT_PROFILE_DETAILS':
return {
profile: action.payload
}
case 'GET_PROFILE_DETAILS': {
return state;
}
default:
return state;
}
}
Try setting up redux in index.android.js instead so it looks something like this:
// Set up redux store above
const Navigator = StackNavigator({
Home: { screen: facebookLogin },
HomePage: { screen: Home },
});
const App = () => (
<Provider store={store}>
<Navigator />
</Provider>
);
AppRegistry.registerComponent('facebookLogin', () => App);