React navigation custom header doesn't disappear when navigating away from screen (iOS only) - react-navigation

I have a stack navigator where one of the screen uses a custom header:
import { createStackNavigator } from "#react-navigation/stack";
import * as React from "react";
import { Button, View } from "react-native";
const Stack = createStackNavigator();
function ScreenA({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: "center"}}>
<Button title="Click me" onPress={() => navigation.navigate("ScreenB")} />
</View>
);
}
function ScreenB({ navigation }) {
return (
<View style={{ flex: 1 , justifyContent: "center"}}>
<Button title="Click me" onPress={() => navigation.navigate("ScreenA")} />
</View>
);
}
function TestComp() {
return (
<Stack.Navigator>
<Stack.Screen
name="ScreenA"
component={ScreenA}
options={{ header: () => <View style={{ height: 160, backgroundColor: "red" }}></View> }}
/>
<Stack.Screen name="ScreenB" component={ScreenB} />
</Stack.Navigator>
);
}
export default TestComp;
As a result, the header of ScreenA (a red bar) is visible from ScreenB. This doesn't happen on Android where the header is properly shown ONLY on ScreenA.
How can I stop the header from ScreenA from showing on ScreenB?

Solved it by using <Stack.Navigator headerMode="screen"> !

Related

React Native Expo (Image Picker) is not displaying the Images

Please help me out with this problem.
I can able to pick the image from my Local storage. In the Console also it is showing, But I cannot able to display it on the screen.
Here is my code.
import * as ImagePicker from "expo-image-picker";
import React, { useState } from "react";
import {
ActivityIndicator,
Button,
FlatList,
Image,
StyleSheet,
Text,
useWindowDimensions,
View
} from "react-native";
import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context";
export default function App() {
const [images, setImages] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const pickImages = async () => {
// No permissions request is necessary for launching the image library
setIsLoading(true);
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
// allowsEditing: true,
allowsMultipleSelection: true,
selectionLimit: 10,
aspect: [4, 3],
quality: 1,
});
setIsLoading(false);
console.log(result);
if (!result.canceled) {
setImages(result.uri ? [result.uri] : result.selected);
}
};
return (
<>
<FlatList
data={images}
renderItem={({ item }) => (
<Image
source={{ uri: item.uri }}
style={{ width: 100, height: 100 }}
/>
)}
keyExtractor={(item) => item.uri}
contentContainerStyle={{ marginVertical: 50, paddingBottom: 50 }}
ListHeaderComponent={
isLoading ? (
<View>
<Text
style={{ fontSize: 20, fontWeight: "bold", textAlign: "center" }}
>
Loading...
</Text>
<ActivityIndicator size={"large"} />
</View>
) : (
<Button title="Pick images" onPress={pickImages} />
)
}
/>
</>
);
}
I can able to pick the image from my Local storage. In Console also it is showing, But i cannot able to display it in screen.
Kindly help me out.
It looks like you're already passing the uri into the array. by doing [result.uri] and then in the image you're doing it again item.uri. trying just doing the item.

Multiple drawers in react-navigation 5.0

I wrote below code to make multiple side menus but it occurred error
Another navigator is already registered for this container. You likely have multiple navigators under a single "NavigationContainer" or "Screen". Make sure each navigator is under a separate "Screen" container.
However I've tried to find the Container for multiple drawers but no luck.
What should I do?
Thanks in advance.
import React from 'react';
import { AppLoading } from 'expo';
import * as Font from 'expo-font';
import { Ionicons } from '#expo/vector-icons';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationNativeContainer } from '#react-navigation/native';
import { Container, Text, Button } from 'native-base';
import 'react-native-gesture-handler'
function BlankScreen({ navigation }) {
return (
<Text>Blank</Text>
);
}
function HomeScreen({ navigation }) {
return (
<Container style={{ flex: 1, flexDirection: 'column-reverse' }}>
<Button onPress={() => navigation.navigate('Menu')}>
<Text>Go to Menu</Text>
</Button>
<Button onPress={() => navigation.navigate('Favorit')}>
<Text>Go to Favorit</Text>
</Button>
</Container>
);
}
function MenuScreen({ navigation }) {
return (
<Container style={{ flex: 1, flexDirection: 'column-reverse' }}>
<Button onPress={() => navigation.goBack()}>
<Text>Go back home</Text>
</Button>
</Container>
);
}
function FavoritScreen({ navigation }) {
return (
<Container style={{ flex: 1, flexDirection: 'column-inverse' }}>
<Button onPress={() => navigation.goBack()}>
<Text>Go back home</Text>
</Button>
</Container>
);
}
const DrawerL = createDrawerNavigator();
const DrawerR = createDrawerNavigator();
export default function App() {
return (
<Container>
<NavigationNativeContainer>
<DrawerL.Navigator initialRouteName="Home" drawerPosition="left">
<DrawerL.Screen name="Home" component={HomeScreen} />
<DrawerL.Screen name="Menu" component={MenuScreen} />
<DrawerL.Screen name="Favorit" component={FavoritScreen} />
</DrawerL.Navigator>
<DrawerR.Navigator initialRouteName="Blank" drawerPosition="right">
<DrawerR.Screen name="Blank" component={BlankScreen} />
<DrawerR.Screen name="Menu" component={MenuScreen} />
<DrawerR.Screen name="Favorit" component={FavoritScreen} />
</DrawerR.Navigator>
</NavigationNativeContainer>
</Container>
);
The way you wrote it won't work, because consider this: you've 2 drawers, one has initial route as "Home", and other as "Blank". Which one should be rendered?
Here, you need to follow the same approach as React Navigation 4, put one drawer inside another:
function HomeStack() {
return (
<Stack.Navigator screenOptions={{ animationEnabled: false }}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Menu" component={MenuScreen} />
<Stack.Screen name="Favorit" component={FavoritScreen} />
</Stack.Navigator>
)
}
function RightDrawer() {
return (
<DrawerR.Navigator initialRouteName="Home" drawerPosition="right">
<DrawerR.Screen name="Home" component={HomeStack} />
<DrawerR.Screen name="Menu" component={MenuScreen} />
<DrawerR.Screen name="Favorit" component={FavoritScreen} />
</DrawerR.Navigator>
)
}
function LeftDrawer() {
return (
<DrawerL.Navigator initialRouteName="RightDrawer" drawerPosition="left">
<DrawerL.Screen name="RightDrawer" component={RightDrawer} />
</DrawerL.Navigator>
);
}
function App() {
return (
<NavigationNativeContainer>
<LeftDrawer />
</NavigationNativeContainer>
)
}
Since your left drawer won't show the screens that you have in Stack, you will need to provide a custom component for your drawer which lists the screens: https://reactnavigation.org/docs/en/next/drawer-navigator.html#providing-a-custom-drawercontent

react native: animation not triggering in release mode

I am trying my react native app on android and iOS using the release mode. unfortunately, there is an animation that doesn't trigger in release mode which works perfectly on debug mode.
i have tried to use the 'useNativeDriver: true' which improved the animation on android but didn't fix the issue
for ref:
"react-native": "0.56.0"
my Drawer.js
toggle = () => {
Animated.timing(this.x_translate, {
toValue: this.state.drawerOpen ? 0 : 1,
duration: this.state.animationDuration,
useNativeDriver: true
}).start();
this.setState({ drawerOpen: !this.state.drawerOpen })
}
render() {
const menu_moveX = this.x_translate.interpolate({
inputRange: [0, 1],
outputRange: [-this.state.width, 0]
});
return (
<Animated.View style={[this.props.style, styles.drawer, {
transform: [
{
translateX: menu_moveX
}
]
}]}>
<ImageBackground
source={require('../images/background.png')}
style={{ width: '100%', height: '100%', alignItems: 'center' }}
>
<View style={styles.blank}></View>
<AutoHeightImage source={require('../images/image.png')} width={0.7 * this.state.width} />
<LineDashboard navigate={this.props.navigate} items={[this.menu[0], this.menu[1]]} sizeIcon={30} />
<LineDashboard navigate={this.props.navigate} items={[this.menu[2], this.menu[3]]} sizeIcon={30} />
<LineDashboard navigate={this.props.navigate} items={[this.menu[4], this.menu[5]]} sizeIcon={30} />
</ImageBackground>
</Animated.View>
)
}
my dashboard.js
componentDidMount() {
this.props.navigation.setParams({
handleThis: () => {
console.log(this.drawer);
this.setState({ loaded: true })
this.drawer.toggle();
}
});
}
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerTitle: 'Home Page',
headerLeft: (
<TouchableOpacity
onPress={() => {
params.handleThis();
}}
style={{ marginLeft: 20 }}
>
<Icon name="menu" size={25} color="black" />
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => {
console.log(navigation);
// navigation.goBack(null)
navigation.navigate('Login');
}}
style={{ marginRight: 20 }}
>
<Text style={{ color: 'red' }}>Log Out</Text>
</TouchableOpacity>
)
}
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.page}>
<ScrollView>
<View style={styles.pageContainer}>
<View style={{ height: 30 }} />
<AutoHeightImage source={require('../images/patient_primary_logo_white.png')} width={0.7 * width} />
<View style={styles.separator} />
<DashboardNotificationTile title={'Your Notifications'} onPress={() => navigate('Notifications')}>
<Text>You have 2 new notifications</Text>
<Text>Please click to see more</Text>
</DashboardNotificationTile>
<DashboardTile title={'Your Visits'}>
<Text>You have 1 upcoming visit</Text>
<SimpleButton onPress={() => navigate('ToBook')} title='View All Visits' width={'100%'} style={{ marginTop: 16 }} color={'green'}/>
</DashboardTile>
<DashboardTile title={'Your Expense Claims'}>
<Text>You have 2 open expense claims</Text>
<SimpleButton onPress={() => navigate('Open')} title='View All Expense Claims' width={'100%'} style={{ marginTop: 16 }} />
</DashboardTile>
</View>
</ScrollView>
<DrawerDashboard navigate={navigate} onRef={(ref) => this.drawer = ref} style={this.state.loaded ? { opacity: 1 } : { opacity: 0 }} />
</View >
)
}
in Dashboard.js, i have a headerLeft that should trigger the function handleThis() which doesn't seems to be doing. however, when pressed, the TouchableOpacity component stay 'selected' rather than coming back to its original state.
any suggestion?
thanks
EDIT:
the issue occurs at any time when the debugger is not on. sorry i just discovered it right now. The animation works perfectly if the remote JS debugger is launched.
So, i thought the issue may be the processing time, as the app is working slower when the debugger is on, maybe my handleThis() function was not loaded...
So, I moved the setParams() from the ComponentDidMount to WillMount.
didn't worked :\
Any suggestion?

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.

React-Native refs undefined on text input

we are currently running React-Native 0.33
We are trying to use the refs to go from 1 text input to another when they hit the next button. Classic username to password.
Anyone have any idea? Below is the code we are using. From other posts I've found on stack this is what they've done; however, it's telling us that this.refs is undefined.
UPDATE
So I've narrowed the problem down to
render() {
return (
<Navigator
renderScene={this.renderScene.bind(this)}
navigator={this.props.navigator}
navigationBar={
<Navigator.NavigationBar style={{backgroundColor: 'transparent'}}
routeMapper={NavigationBarRouteMapper} />
} />
);
}
If I just render the code below in the renderScene function inside of the original render it works, however with the navigator it won't work. Does anyone know why? Or how to have the navigator show as well as render the code in renderScene to appear in the original render?
class LoginIOS extends Component{
constructor(props) {
super(props);
this.state={
username: '',
password: '',
myKey: '',
};
}
render() {
return (
<Navigator
renderScene={this.renderScene.bind(this)}
navigator={this.props.navigator}
navigationBar={
<Navigator.NavigationBar style={{backgroundColor: 'transparent'}}
routeMapper={NavigationBarRouteMapper} />
} />
);
}
renderScene() {
return (
<View style={styles.credentialContainer}>
<View style={styles.inputContainer}>
<Icon style={styles.inputPassword} name="person" size={28} color="#FFCD00" />
<View style={{flexDirection: 'row', flex: 1, marginLeft: 2, marginRight: 2, borderBottomColor: '#e0e0e0', borderBottomWidth: 2}}>
<TextInput
ref = "FirstInput"
style={styles.input}
placeholder="Username"
autoCorrect={false}
autoCapitalize="none"
returnKeyType="next"
placeholderTextColor="#e0e0e0"
onChangeText={(text) => this.setState({username: text})}
value={this.state.username}
onSubmitEditing={(event) => {
this.refs.SecondInput.focus();
}}
>
</TextInput>
</View>
</View>
<View style={styles.inputContainer}>
<Icon style={styles.inputPassword} name="lock" size={28} color="#FFCD00" />
<View style={{flexDirection: 'row', flex: 1, marginLeft: 2, marginRight: 2, borderBottomColor: '#e0e0e0', borderBottomWidth: 2}}>
<TextInput
ref = "SecondInput"
style={styles.input}
placeholder="Password"
autoCorrect={false}
secureTextEntry={true}
placeholderTextColor="#e0e0e0"
onChangeText={(text) => this.setState({password: text})}
value={this.state.password}
returnKeyType="done"
onSubmitEditing={(event)=> {
this.login();
}}
focus={this.state.focusPassword}
>
</TextInput>
</View>
</View>
</View>
);
}
Try setting the reference using a function. Like this:
<TextInput ref={(ref) => { this.FirstInput = ref; }} />
Then you can access to the reference with this.FirstInput instead of this.refs.FirstInput
For a functional component using the useRef hook. You can use achieve this easily, with...
import React, { useRef } from 'react';
import { TextInput, } from 'react-native';
function MyTextInput(){
const textInputRef = useRef<TextInput>(null);;
return (
<TextInput ref={textInputRef} />
)
}
Try changing the Navigator's renderScene callback to the following (based on Navigator documentation) cause you will need the navigator object later.
renderScene={(route, navigator) => this.renderScene(route, navigator)}
Then, use 'navigator' instead of 'this' to get the refs.
navigator.refs.SecondInput.focus()

Resources