React Native: Saving image URI via AsyncStorage and reloading in different React Navigation screen - image

I am currently trying to combine a React Native Camera example with the React Navigation v2 and want to take a picture in the first view (called CameraView), save said picture to AsyncStorage, navigate to a second view (called GalleryView) and render this picture from AsyncStorage into an image tag.
I am using RN 0.57.1, RN-Camera 1.3.1, React Navigation 2.18.0 on a Windows 10 computer emulating an Android phone running Android version 8.0.0.
This is the code for the two views:
CameraView.js:
import React from "react";
import {
AsyncStorage,
Dimensions,
StyleSheet,
TouchableHighlight,
View
} from "react-native";
import { RNCamera as Camera } from "react-native-camera";
const styles = StyleSheet.create({
preview: {
flex: 1,
justifyContent: "flex-end",
alignItems: "center",
height: Dimensions.get("window").height,
width: Dimensions.get("window").width
},
capture: {
width: 70,
height: 70,
borderRadius: 35,
borderWidth: 5,
borderColor: "#FFF",
marginBottom: 15
}
});
class CameraView extends React.Component {
static navigationOptions = ({ navigation }) => ({
header: null
});
constructor(props) {
super(props);
this.state = {
imageUri: null
};
}
takePicture = async () => {
try {
const imageData = await this.camera.takePictureAsync({
fixOrientation: true
});
this.setState({
imageUri: imageData.uri
});
this._saveImageAsync();
} catch (err) {
console.log("err: ", err);
}
};
_saveImageAsync = async () => {
await AsyncStorage.setItem("imageUri", this.state.imageUri);
this.props.navigation.navigate("GalleryView");
};
render() {
return (
<Camera
ref={cam => {
this.camera = cam;
}}
style={styles.preview}
flashMode={Camera.Constants.FlashMode.off}
permissionDialogTitle={"Permission to use camera"}
permissionDialogMessage={
"We need your permission to use your camera phone"
}
>
<TouchableHighlight
style={styles.capture}
onPress={this.takePicture.bind(this)}
underlayColor="rgba(255, 255, 255, 0.5)"
>
<View />
</TouchableHighlight>
</Camera>
);
}
}
export default CameraView;
GalleryView.js:
import React from "react";
import {
AsyncStorage,
Button,
Dimensions,
StyleSheet,
Text,
Image,
View
} from "react-native";
const styles = StyleSheet.create({
preview: {
flex: 1,
justifyContent: "flex-end",
alignItems: "center",
height: Dimensions.get("window").height,
width: Dimensions.get("window").width
},
cancel: {
position: "absolute",
right: 20,
top: 20,
backgroundColor: "transparent",
color: "#FFF",
fontWeight: "600",
fontSize: 17
}
});
class GalleryView extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: "Seismic"
});
constructor(props) {
super(props);
AsyncStorage.getItem("imageUri").then(response => {
this.setState({
imageUri: response
});
});
}
render() {
return (
<View>
<Image source={{ uri: this.state.imageUri }} style={styles.preview} />
<Text
style={styles.cancel}
onPress={() => this.state.setState({ imageData: null })}
>
X
</Text>
<Button
title="Map View"
onPress={() => this.props.navigation.popToTop()}
/>
</View>
);
}
}
export default GalleryView;
The first-mentioned example works fine, but when trying to use the AsyncStorage I get the error below after snapping the image and executing navigate() to the second view.
TypeError: TypeError: null is not an object (evaluating
'this.state.imageUri')
This error is located at:
in GalleryView (at SceneView.js:9)
in SceneView (at StackViewLayout.js:478)
in RCTView (at View.js:44)
in RCTView (at View.js:44)
in RCTView (at View.js:44)
in AnimatedComponent (at screens.native.js:58)
in Screen (at StackViewCard.js:42)
in Card (at createPointerEventsContainer.js:26)
in Container (at StackViewLayout.js:507)
in RCTView (at View.js:44)
in ScreenContainer (at StackViewLayout.js:401)
in RCTView (at View.js:44)
in StackViewLayout (at withOrientation.js:30)
in withOrientation (at StackView.js:49)
in RCTView (at View.js:44)
in Transitioner (at StackView.js:19)
in StackView (at createNavigator.js:57)
in Navigator (at createKeyboardAwareNavigator.js:11)
in KeyboardAwareNavigator (at createNavigationContainer.js:376)
in NavigationContainer (at routes.js:39)
in Routes (at renderApplication.js:34)
in RCTView (at View.js:44)
in RCTView (at View.js:44)
in AppContainer (at renderApplication.js:33)
>
This error is located at:
in NavigationContainer (at routes.js:39)
in Routes (at renderApplication.js:34)
in RCTView (at View.js:44)
in RCTView (at View.js:44)
in AppContainer (at renderApplication.js:33) render
C:\Users\msteinbrink\Safeguard\seismic-app\src\screens\GalleryView.js:25:11
proxiedMethod
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-proxy\modules\createPrototypeProxy.js:44:35
finishClassComponent
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:10563:21
updateClassComponent
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:10505:4
beginWork
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:11338:8
performUnitOfWork
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:14091:21
workLoop
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:14129:41
renderRoot
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:14226:15
performWorkOnRoot
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:15193:17
performWork
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:15090:24
performSyncWork
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:15047:14
requestWork
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:14925:19
scheduleWork
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:14711:16
enqueueSetState
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Renderer\oss\ReactNativeRenderer-dev.js:7700:17
setState
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react\cjs\react.development.js:372:31
dispatch
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-navigation\src\createNavigationContainer.js:342:22
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-navigation\src\getChildNavigation.js:56:33
_callee2$
C:\Users\msteinbrink\Safeguard\seismic-app\src\screens\CameraView.js:88:16
tryCatch
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\#babel\runtime\node_modules\regenerator-runtime\runtime.js:62:44
invoke
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\#babel\runtime\node_modules\regenerator-runtime\runtime.js:288:30
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\#babel\runtime\node_modules\regenerator-runtime\runtime.js:114:28
tryCatch
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\#babel\runtime\node_modules\regenerator-runtime\runtime.js:62:44
invoke
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\#babel\runtime\node_modules\regenerator-runtime\runtime.js:152:28
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\#babel\runtime\node_modules\regenerator-runtime\runtime.js:162:19
tryCallOne
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\promise\setimmediate\core.js:37:14
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\promise\setimmediate\core.js:123:25
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:295:23
_callTimer
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:152:14
_callImmediatesPass
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:200:17
callImmediates
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:464:30
__callImmediates
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:320:6
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:135:6
__guard
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:297:10
flushedQueue
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:134:17
invokeCallbackAndReturnFlushedQueue
C:\Users\msteinbrink\Safeguard\seismic-app\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:130:11
I would appreciate if someone could point out how to properly use AsyncStorage with React Navigation to render a previously saved image from React Native Camera. As you probably could tell, I am fairly new to React Native, so please tell me if I got the concept completely wrong or anything.
Thanks in advance!

Thanks to Wainages comment, I made it work. I added the state isLoaded in GalleryView and show just the text "Loading" before the async operation is done.
import React from "react";
import {
AsyncStorage,
Dimensions,
StyleSheet,
Text,
Button,
Image,
View
} from "react-native";
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
backgroundColor: "#000000"
},
preview: {
flex: 1,
justifyContent: "flex-end",
alignItems: "center",
height: Dimensions.get("window").height,
width: Dimensions.get("window").width
},
cancel: {
position: "absolute",
right: 20,
top: 20,
backgroundColor: "transparent",
color: "#FFF",
fontWeight: "600",
fontSize: 17
}
});
class GalleryView extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: "Seismic"
});
constructor(props) {
super(props);
this.state = {
imageUri: null,
isLoaded: false
};
AsyncStorage.getItem("imageUri").then(response => {
this.setState({
isLoaded: true,
imageUri: response
});
});
}
renderImage() {
return (
<View>
<Image source={{ uri: this.state.imageUri }} style={styles.preview} />
<Text
style={styles.cancel}
onPress={() => this.setState({ path: null })}
>
X
</Text>
</View>
);
}
renderLoadingScreen() {
return (
<View>
<Text style={styles.cancel}>Loading</Text>
<Button
title="Map View"
onPress={() => this.props.navigation.popToTop()}
/>
</View>
);
}
render() {
return (
<View style={styles.container}>
{this.state.isLoaded ? this.renderImage() : this.renderLoadingScreen()}
</View>
);
}
}
export default GalleryView;

Related

Image not showing in React Native Web App. Appears when I build for Android or iOS but doesn't show when using react-scripts start

I am trying to display an image in a React Native Web App which is run using react-scripts start. When I build the App for iOS or Android, the image appears perfectly fine (using expo) but when I build it for the Web App, the image fails to load. Here is the code snippet for the Home component where the image is loaded
import React from "react";
import { ScrollView, ActivityIndicator, StyleSheet, Image, ImageBackground } from "react-native";
import UserList from "./user-list";
import Header from './header';
import sanityClient from './assets/client'
import BackButton from './back-button'
import User from './user'
// import {Asset} from 'expo-asset';
// const imageURI = Asset.fromModule(require('./arrow.png')).uri;
const image = require('./assets/aoeu.jpg');
class Home extends React.Component {
state = {
user: {},
loading: true
};
componentDidMount() {
// TODO: get users
this.getUser();
}
async getUser() {
sanityClient.fetch(`*[ _type == "user" && emailAddress.current == "dwight#viamaven.com"]`)
.then((data) => {
console.log(data);
this.setState({user: data[0], loading: false});
console.log(this.state.user);
})
.catch((err) => console.error(err))
// const res = await fetch("https://randomuser.me/api/?results=20");
// const { results} = await res.json();
// // console.log(results)
// this.setState({users: [...results], loading: false});
}
render() {
return (
<ScrollView
noSpacer={true}
noScroll={true}
style={styles.container}
showVerticalSCrollIndicator = {false}
showHorizontalScrollIndicator = {false}
>
{this.state.loading ? (
<ActivityIndicator
style={[styles.centering, styles.gray]}
color="#5d38aa"
size="large"
/>
) : (
<View>
<Header title={this.state.user.name} />
<View>
<Image
source={require('./arrow.png')}
style={styles.image}
/>
</View>
<User />
</View>
)}
</ScrollView>
);
}
}
var styles = StyleSheet.create({
container: {
backgroundColor: "white",
width: '375px',
height: '812px',
// top: '50px',
},
centering: {
alignItems: "center",
justifyContent: "center",
padding: 8,
height: '100vh'
},
image: {
width: '50px',
height: '50px',
marginRight: 20,
boxShadow: "0 1px 2px 0 rgba(0,0,0,0.1)"
}
});
export default Home;
Here is a link to the GitHub repo where the entire project is stored https://github.com/nimbusdin/stackreactnative
Try to import this way and use it like this
import image = './assets/aoeu.jpg';
<Image
source={image}
style={styles.image}
/>

Replace with a text while encountering onError on Image REACT NATIVE

Just like HTML, we have the opportunity to show a text i.e.
<img src="hello.png" alt="hello" />
There is plenty of suggestions for replacing an image with another image (fallback src), but I need to show a text instead of any other images!
React Native Image onError method will execute on Image when image not found on server or unexpectedly something wrong goes with the connection. Using that you can display text as below,
import React, { Component } from "react";
import { Text, View, Image, StyleSheet } from "react-native";
export default class Example extends Component {
state = {
isLoadingImage: true,
isImageFailed: false
};
onErrorLoadingImage = () => {
this.setState({
isLoadingImage: false,
isImageFailed: true
});
};
render() {
return (
<View style={styles.container}>
{!this.state.isImageFailed ? (
<Image
source={{
uri:
"https://reactnativecode.com/wp-content/uploads/2017/10/Guitar.jpg"
}}
style={styles.imageStyle}
onError={this.onErrorLoadingImage}
/>
) : (
<View style={styles.container}>
<Text>Error loading image</Text>
</View>
)}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 10,
justifyContent: "center",
alignItems: "center"
},
imageStyle: {
resizeMode: "center",
width: "50%",
height: "50%"
}
});
Unfortunately, there is no other way to display text just like HTML alt=" Error loading image "
Hope this will help you.

React native Drawer only after login

I have snack link here
https://snack.expo.io/#mparvez19861/drawer-navigation?session_id=snack-session-hyLuO4xPa
I am trying to show drawer only if user login, for login there will not be any drawer.
Please help
Thanks
You can accomplish this by wrapping your Drawer Navigator in a Switch Navigator. Upon logging in, the Switch Navigator will switch from the login screen (which is just a screen), to the main App screen (which is initialized with createDrawerNavigator).
import React, { Component } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import {
createSwitchNavigator,
createStackNavigator,
createAppContainer,
createDrawerNavigator,
} from 'react-navigation';
class Screen extends Component {
render() {
return (
<View style={styles.container}>
<Text>Screen</Text>
</View>
);
}
}
class AuthScreen extends Component {
render() {
return (
<View style={styles.container}>
<Text>Auth Screen</Text>
<TouchableOpacity onPress={() => this.props.navigation.navigate('App')}>
<Text>Login</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
marginTop: 40,
justifyContent: 'center',
alignItems: 'center',
},
});
const SomeStackNavigator = createStackNavigator({
ScreenA: Screen,
ScreenB: Screen,
});
const AppStack = createDrawerNavigator({
StackA: {
name: 'StackA',
screen: SomeStackNavigator,
},
StackB: {
name: 'StackB',
screen: SomeStackNavigator,
},
});
const AppNavigator = createSwitchNavigator(
{
App: AppStack,
Auth: {
screen: AuthScreen,
},
},
{
initialRouteName: 'Auth',
},
);
const AppContainer = createAppContainer(AppNavigator);
export default AppContainer;
Check it out here.

React Native Audio Animation

I am currently using React Native Audio to record audio and storing the sound. I am wondering if there is anyway to get the progress of this recording in realtime to map the microphone input to an animation, to give a visual feedback that the microphone is working.
Currently, the onProgress() function of the package only sends, from what I've found, only the current timecode.
Thanks for any help!
I am currently working on somthing similer.
That how i done it.
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
Button,
LayoutAnimation,
Image,
ScrollView,
Animated
} from 'react-native';
export default class App extends Component {
state = {
isPressed: false,
animated: new Animated.Value(0),
opacityA: new Animated.Value(1),
}
constructor(props) {
super(props);
this._onPress = this._onPress.bind(this);
}
_runAnimation() {
const { animated, opacityA } = this.state;
Animated.loop(
Animated.parallel([
Animated.timing(animated, {
toValue: 1,
duration: 1000,
}),
Animated.timing(opacityA, {
toValue: 0,
duration: 1000,
})
])
).start();
}
_stopAnimation() {
Animated.loop(
Animated.parallel([
Animated.timing(animated),
Animated.timing(opacityA)
])
).stop();
}
_onPress() {
this.setState(
state => ({ isPressed: !state.isPressed }),
)
}
_micButton() {
const { isPressed, animated, opacityA, } = this.state;
if (isPressed) {
//some function
this._runAnimation();
return (
<Animated.View style={{
width: 100,
height: 100,
borderRadius: 50,
backgroundColor: 'rgba(153,0,0,0.4)',
opacity: opacityA,
transform: [
{
scale: animated
}
]
}}>
{/* icon or image */}
</Animated.View>
);
} else {
//some function
return (
<View style={{
width: 100,
height: 100,
borderRadius: 50,
backgroundColor: 'rgba(153,0,0,0.4)',
}}>
{/* icon or image */}
</View>
);
}
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._onPress}>
{this._micButton()}
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
i hope that solve your problem.
I use expo-av. There is an API setOnRecordingStatusUpdate, you can set a function that is called regularly with the status of the Recording. For example,
async startRecording(callback: onRecordingStatusUpdated) {
this.isLoading = true
await Audio.setAudioModeAsync({
allowsRecordingIOS: true,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
shouldDuckAndroid: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
playThroughEarpieceAndroid: true
});
if (this.recording !== null) {
// only one recorder is allowed to exist
this.recording.setOnRecordingStatusUpdate(null);
this.recording = null;
}
const recording = new Audio.Recording();
await recording.prepareToRecordAsync(this.recordingSettings);
// ✨✨✨set the callback
recording.setOnRecordingStatusUpdate(callback);
this.recording = recording;
await this.recording.startAsync(); // Will call this._updateScreenForRecordingStatus to update the screen.
this.isLoading = false
}
// 🌟🌟🌟
startRecording((status => console.log('[onRecording]', status)))

Unable to store values in asyncStorage in React Native

The value set in asyncstorage is null and also the log for usertext is { [TypeError: undefined is not an object (evaluating 'this.state.usertext')] line: 30, column: 34 }
what might be the problem here?
'use strict'
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Navigator,
Text,
TextInput,
Button,
AsyncStorage,
View
} from 'react-native';
import { Actions } from 'react-native-router-flux';
import api from './utilities/api';
export default class LoginScreen extends Component{
constructor(props) {
super(props);
this.state = { usertext: 'placeholder' , passwordtext: 'placeholder' ,buttonvalue: '
Login'};
}
async login() {
const res = await api.getLoginResult();
const status= res.status;
console.log('log'+status);
if(status==='success'){
try {
console.log('usertext'+this.state.usertext);
await AsyncStorage.setItem('username', this.state.usertext);
var username = await AsyncStorage.getItem('username');
console.log('username'+username);
await AsyncStorage.setItem('password', this.state.passwordtext);
} catch (error) {
// Error saving data
console.log(error);
}
Actions.HomeScreen();
}
}
render() {
return (
<View style={styles.container}>
<View style={styles.containerLogin}>
<View style={styles.headerContainer}>
<Text style={styles.welcome}>PLEASE LOGIN</Text>
</View>
<View style={styles.inputlayouts}>
<TextInput
onChangeText={(usertext) => this.setState({usertext})}
value={this.state.usertext}
/>
<TextInput
onChangeText={(passwordtext) => this.setState({passwordtext})}
value={this.state.passwordtext}
/>
</View>
<View style={styles.buttonView}>
<Button
style={styles.buttonstyle}
onPress={this.login}
title={this.state.buttonvalue}
color="#FF7323">
</Button>
</View>
</View>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'stretch',
backgroundColor: '#1AB591',
},
headerContainer:{
backgroundColor:'#FF7323'
},
buttonView:{
marginLeft:15,
marginRight:15,
marginBottom:15
},
inputlayouts:{
marginLeft:6,
marginRight:6,
marginBottom:10,
},
containerLogin: {
justifyContent: 'center',
alignItems: 'stretch',
backgroundColor: '#FFF',
marginLeft:20,
marginRight:20
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
color:'#FFF'
},
buttonstyle: {
width:20,
}
});
Change
<Button
style={styles.buttonstyle}
onPress={this.login}
title={this.state.buttonvalue}
color="#FF7323">
</Button>
to
<Button
style={styles.buttonstyle}
onPress={this.login.bind(this)}
title={this.state.buttonvalue}
color="#FF7323">
</Button>
When you do onPress={this.login}, the method reference to this will be different than your component. Binding this to method, allows you to reference your this in the function.

Resources