How to render react native component in the background? I want a splash screen, but the current example only allows async downloading data.
I have tried SplashScreen of AppLoading in Expo, but it only allows to download data first, it doesn't allow for async render for components.
import React from 'react';
import { Image, Text, View } from 'react-native';
import { Asset, SplashScreen } from 'expo';
export default class App extends React.Component {
state = {
isReady: false,
};
componentDidMount() {
SplashScreen.preventAutoHide();
}
render() {
if (!this.state.isReady) {
return (
<View style={{ flex: 1 }}>
<Image
source={require('./assets/images/splash.gif')}
onLoad={this._cacheResourcesAsync}
/>
</View>
);
}
return (
// ...
);
}
_cacheResourcesAsync = async () => {
SplashScreen.hide();
// ....
await Promise.all(cacheImages);
this.setState({ isReady: true });
}
}
This only download data asynchronously, but I want to render asynchronously.
Related
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>
);
}
I'm using Expo and React Native, but can't download PDF file and save it to filesystem.
I don't find any examples and exact answer.
Is it possible to download PDF and then save it to docs?
Your goal can be achieved using expo-media-library and expo-file-system. Have a try this.
import React from 'react';
import { View, Button } from 'react-native';
import * as FileSystem from 'expo-file-system';
import * as Permissions from 'expo-permissions';
import * as MediaLibrary from 'expo-media-library';
class TestClass extends React.Component {
constructor(props) {
super(props)
}
async saveFile() {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status === "granted") {
FileSystem.downloadAsync("http://www.pdf995.com/samples/pdf.pdf", FileSystem.documentDirectory + 'test.pdf')
.then( async ({uri}) => {
await MediaLibrary.createAssetAsync(uri)
}).catch((err) => {
console.log(err)
})
}
}
render(){
return(
<View style={{flex : 1, justifyContent : 'center', alignItems: 'center'}}>
<Button title="Create PDF" onPress={() => this.saveFile()}/>
</View>
)
}
}
export default TestClass
I know this question has been asked several times but nowhere i found the answer. So I thought I will elaborate my question. Everything works fine in development. But when I make the standalone APK the splash screen doesn't goes away. I even tried to hide it after the component is loaded. That also doesn't work. What could be the issue ?
import { SplashScreen } from 'expo';
componentDidMount() {
setTimeout(function(){
SplashScreen.hide();
},2000);
}
try it:
create a screen: SplashScreen.js
and write the code bellow:
import React, { Component } from 'react';
import { View, StatusBar, Image } from 'react-native';
import introImage from '../assets/IntroPin3.gif';
export default class Loading extends Component {
render() {
return (
<View style={{ flex: 1, justifyContent:'center', alignItems:'center', backgroundColor: '#FDFDFF' }}>
<StatusBar hidden />
<Image
style={{height:350, width: 350 }}
source={ introImage}
/>
</View>
);
}
}
and in your app.js
write it:
import React, { Component } from 'react';
import SplashScreen from './Components/SplashScreen.js';
import Intro from './Components/Menu.js';
export default class Index extends Component {
state = {
ready: false,
};
componentDidMount() {
setTimeout(() => {
this.setState({ ready: true });
}, 5530);
}
render() {
if (this.state.ready === false) {
return <SplashScreen />;
}
return <Intro />;
}
}
try it, if this help you let me know.
import SplashScreen from 'react-native-splash-screen'
export default class WelcomePage extends Component {
componentDidMount() {
// do stuff while splash screen is shown
// After having done stuff (such as async tasks) hide the splash screen
SplashScreen.hide();
}
}
try this one... also you can read the documentation:
https://github.com/crazycodeboy/react-native-splash-screen
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>)
);
}
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?