React Navigation Shared Element 5 Nested Navigators - reactjs

I'm running into a little issue with react-navigation-shared-element 5, so I made a very basic app to show an example. Basically I have a tab navigator nested in a stack navigator and when navigating to the tab navigator I'd like for a shared element transition. This is what I have so far. The transition was working when I had both MainScreen and OtherScreen in the Stack Navigator, but when I moved OtherScreen to the tab navigator it stopped.
import {NavigationContainer} from '#react-navigation/native';
import MainScreen from './src/MainScreen';
import OtherScreen from './src/OtherScreen';
import OtherScreenSecond from './src/OtherScreenSecond';
import {createSharedElementStackNavigator} from 'react-navigation-shared-element';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const Stack = createSharedElementStackNavigator();
const Tab = createBottomTabNavigator();
const TabStuff = () => {
return (
<Tab.Navigator>
<Tab.Screen name="OtherScreen" component={OtherScreen} />
<Tab.Screen name="OtherScreenSecond" component={OtherScreenSecond} />
</Tab.Navigator>
);
};
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator mode="modal" headerMode="none">
<Stack.Screen name="MainScreen" component={MainScreen} />
<Stack.Screen name="Tabs" component={TabStuff} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
import {View, Text, StyleSheet, TouchableOpacity} from 'react-native';
import {SharedElement} from 'react-navigation-shared-element';
const MainScreen = (props) => {
return (
<View style={{...styles.container}}>
<SharedElement id="test">
<View
style={{
width: 100,
height: 100,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 50,
backgroundColor: 'orange',
marginBottom: 50,
}}></View>
</SharedElement>
<TouchableOpacity onPress={() => props.navigation.navigate('Tabs')}>
<Text>Next</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'red',
},
});
export default MainScreen;
import {View, Text, StyleSheet, TouchableOpacity} from 'react-native';
import {SharedElement} from 'react-navigation-shared-element';
const OtherScreen = (props) => {
return (
<View style={{...styles.container}}>
<SharedElement id="test">
<View
style={{
width: 150,
height: 150,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 75,
backgroundColor: 'blue',
marginBottom: 50,
}}></View>
</SharedElement>
<TouchableOpacity onPress={() => props.navigation.pop()}>
<Text>Back</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'yellow',
},
});
OtherScreen.sharedElements = (route, otherRoute, showing) => {
return ['test'];
};
export default OtherScreen;
Any pointers would be greatly appreciated, thanks!

You have to add sharedElementsConfig to Stack. Screen specifying the id of the shared element to work while navigating back
Change code to:
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator mode="modal" headerMode="none">
<Stack.Screen
name="MainScreen"
sharedElementsConfig={(props) => [
{
id: 'test', animation: 'fade'
}
]}
component={MainScreen} />
<Stack.Screen name="Tabs" component={TabStuff} />
</Stack.Navigator>
</NavigationContainer>
);
};

Related

Expo app crashing on android on rendering components

When i render a screen in App.js the app crashes on android but works fine on android. If i render some function defined in App.js in renders on screen.
App.js
import React from 'react';
import {enableScreens} from 'react-native-screens';
enableScreens();
import { ActivityIndicator, View, Text, Button, TextInput, StyleSheet, TouchableOpacity } from "react-native";
import { NavigationContainer } from "#react-navigation/native";
import {createNativeStackNavigator} from "#react-navigation/native-stack";
import Main from "./screens/Main"
function HomeScreen({navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<TouchableOpacity onPress={() => {navigation.navigate("LoginPage")}}>
<Text>NEXT</Text>
</TouchableOpacity>
</View>
);
}
function S1({navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<TouchableOpacity onPress={() => {navigation.navigate("main")}}>
<Text>NEXT</Text>
</TouchableOpacity>
</View>
);
}
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" options={{headerShown : false}} component={HomeScreen} />
<Stack.Screen name="LoginPage" options={{headerShown : false}} component={S1} />
<Stack.Screen name="main" options={{headerShown : false}} component={Main} />
</Stack.Navigator>
</NavigationContainer>
);
}
Main.js
import react, {useState} from "react";
import { View, Text, Button, TextInput, StyleSheet, TouchableOpacity } from "react-native";
export default function Main({navigation}){
const options = [
{
name: "Games",
redirect: "Games"
},
{
name : "AR Tutorials",
redirect: "AR"
},
{
name : "Competition",
redirect : "Competition"
}
];
return(
<View style={styles.container}
>
{
options.map(op => (
<TouchableOpacity
style={styles.card}key={op.name} onPress={() => {navigation.navigate(op.redirect)}}>
<Text style={styles.cardText}>{op.name}</Text>
</TouchableOpacity>
))
}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
card: {
backgroundColor: "rgb(0, 100, 255)",
minHeight: "25%",
minWidth: "70%",
borderRadius: 1,
marginVertical: "1rem",
display: "flex",
justifyContent: "flex-end",
alignItems: "center"
},
cardText : {
marginBottom: "0.5rem"
}
});
Here functions HomeScreen and S1 renders but on navigating to Main the app crashes whiteout any logs.
I have tried building apk ind then running but it shows same behaviour.

Adding Custom header in react-navigation 5

I know how to implement header in navigation 3, but I'm having trouble passing the navigation to my header component in version 5.
(HomeStack.js)
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { Button } from 'react-native';
import Home from '../screens/home';
import ReviewDetails from '../screens/reviewDetails';
import Header from '../shared/header';
const Stack = createStackNavigator()
export default Navigator = () => {
return (
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<Stack.Screen
name='Home'
component={Home}
options={{
headerTitle: ({navigation}) => <Header navigation={navigation}/>
}}
/>
<Stack.Screen name='ReviewDetails' component={ReviewDetails} />
</Stack.Navigator>
)
}
(header.js)
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { MaterialIcons } from '#expo/vector-icons';
export default function Header({ navigation }) {
const openMenu = () => {
navigation.openDrawer();
}
return (
<View style={styles.header}>
<MaterialIcons name='menu' size={28} onPress={openMenu} style={styles.icon}/>
<View>
<Text style={styles.headerText}>GameZone</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
header: {
width: '100%',
height: '100%',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center'
},
headerText: {
fontWeight: 'bold',
fontSize: 20,
color: '#333',
letterSpacing: 1,
},
icon: {
position: 'absolute',
left: 16
}
});
I think the only problem is how to pass the navigation var. to the header component in order to open and close the drawer. I am struggling with this problem. Your help is appreciated.

React Native Navigation (ver4.x) unable to get props in custom header component. Keep getting undefined error

I have a custom header in my app and I am trying to get the navigation to open the Drawer when clicked on the menu item in the header component. I've passed the navigation and the header text as props to the component. However the props are being returned as undefined.
This is my header
import React from 'react';
import {View, StyleSheet, Text, Image, Alert, TouchableOpacity } from 'react-native';
import Colors from '../constants/Colors';
import { MaterialIcons } from '#expo/vector-icons';
const Header = ({navigation, headerText}) => {
const openMenu = () => {
console.log({headerText}) **/// this prints Object { "headerText": undefined,}**
navigation.openDrawer() **/// this also throws an undefined error**
}
console.log(headerText)**/// this displays the headerText correct on loading**
return (
<View style={styles.header} >
<MaterialIcons onPress={openMenu} name='menu' size={30} style={styles.icon} />
<Text style={styles.logo}>My Home Page Header</Text>
</View>
);
};
const styles = StyleSheet.create({
header: {
paddingTop: 20,
width: '100%',
height: '10%',
flexDirection: 'row',
},
logo: {
height: '100%',
width: '90%',
fontFamily: 'pacifico-regular',
fontSize: 28,
paddingTop: 20,
paddingLeft: 20,
},
icon: {
marginTop:30,
paddingLeft: 10,
},
});
export default Header;
console.log(headerText)/// this displays the header text correctly when loading the component.
However trying to use the props in the Text or the View throws an undefined error.
Clicking on the MaterialIcon menu icon gives the error :
Object {
"headerText": undefined,
}
and
TypeError: undefined is not an object (evaluating 'navigation.openDrawer')
This is the homestack.js
import { createStackNavigator } from 'react-navigation-stack';
import NewFeaturedRecipes from '../screens/NewFeaturedRecipes';
import NewRecipeDetails from '../screens/NewRecipeDetails';
import Header from '../shared/Header';
import React from 'react';
import {navigation} from 'react-navigation'
// const navigation = navigation;
const screens = {
NewFeaturedRecipes: {
screen: NewFeaturedRecipes,
navigationOptions: ({navigation}) => {
return {
headerTitle: () => <Header navigation={navigation}
headerText='Testting headertext from hoomestack' />
}
}
},
NewRecipeDetails: {
screen: NewRecipeDetails,
navigationOptions: {
title: 'Recipe Details',
}
},
}
const HomeStack = createStackNavigator(screens, {
defaultNavigationOptions: {
headerStyle: {
height: 20,
},
}
});
export default (HomeStack);
Here the answer if anybody is interested. I've used React Navigation 5.x for the nested navigators and I am able to call the openDrawer() from the menu in the Header component.
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View, Button, DrawerLayoutAndroid } from 'react-native';
import { NavigationContainer, useNavigation } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import {createDrawerNavigator} from '#react-navigation/drawer';
import { MaterialIcons } from '#expo/vector-icons';
const Header = ({ headerText }) => {
const navigation = useNavigation()
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', flexDirection: 'row' }}>
<MaterialIcons onPress={()=> navigation.openDrawer()} name='menu' size={30} style={styles.icon} />
<Text>Header Text = {headerText}</Text>
</View>
)
}
const HomeScreen = ({ navigation }) => {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button onPress={() => navigation.navigate('Details')} title='Goto Details Screen' />
</View>
)
}
const DetailScreen = ({ navigation }) => {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Detail Screen</Text>
<Button onPress={() => navigation.navigate('Home')} title='Goto Home Screen' />
</View>
)
}
const HomeStack = () => {
return (
<Stack.Navigator>
<Stack.Screen
name='Home'
component={HomeScreen}
options={{
headerTitle: props => <Header headerText='Text from App' />
}}
/>
<Stack.Screen name='Details' component={DetailScreen} options={{
headerTitle: props => <Header headerText='Text from Details' />
}}
/>
</Stack.Navigator>
)
}
function NotificationsScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', flexDirection:'column' }}>
<Text>Notifications Screen</Text>
<Button onPress={() => navigation.goBack()} title="Go back home" />
</View>
);
}
const Stack = createStackNavigator()
const Drawer = createDrawerNavigator()
const App = () => {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName='Home'>
<Drawer.Screen name='Home' component={HomeStack}/>
<Drawer.Screen name='Notifications' component={NotificationsScreen}/>
</Drawer.Navigator>
</NavigationContainer>
)
}
export default App
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

how to make initialParams updated

I want to update initialParams of react native navigation on clicking of the menu item
import React, { useState } from 'react';
import {createStackNavigator} from '#react-navigation/stack'
import { NavigationContainer } from '#react-navigation/native';
import userMain from './../../components/users/userMain';
import { View, Icon } from 'native-base';
export const UserStack = () => {
const Stack = new createStackNavigator()
const [toggleSearch, setToggleSearch] = useState(true)
const changeStyleToggle = () => {
setToggleSearch( !toggleSearch )
// console.log('toggle search here ==== ', toggleSearch)
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="User Homepage"
component={userMain}
initialParams={{ toggleSearch: toggleSearch}}
options = {{
title: 'My home',
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
headerRight: () => (
<View style={{flexDirection:'row', justifyContent:'space-evenly', width:120}}>
<Icon name='home' onPress={() => changeStyleToggle()} />
<Icon name='home' />
<Icon name='home' />
</View>
),
}}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
the component i am calling is userMain where i am calling initialParams value and on behalf of that i want to change style
import React from 'react'
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'
import { Input } from 'native-base';
const userMain = (props) => {
const {toggleSearch} = props.route.params;
console.log(toggleSearch)
const check = toggleSearch == true ? 'true!!' : 'false!!'
return (
<View style={styles.container}>
<Input
placeholder="search"
style={styles.searchInput}
/>
<Text>user homepage here</Text>
<Text>{check}</Text>
<TouchableOpacity style={styles.btn}>
<Text style={styles.btnText}> + </Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
container: {
alignItems: 'center', flex: 1, justifyContent: 'center',
},
btn: {
position: 'absolute', justifyContent: 'center', alignItems: 'center',
bottom:10, right:10, width:60, height: 60,
backgroundColor: '#fff', borderRadius: 50, borderColor: '#000',borderWidth:1,
},
btnText: {
fontSize: 50, color: 'black',
},
searchInput: {
position:'absolute', top: 0, borderWidth: 1, width: 300, opacity:0
}
})
export default userMain
i have checked on clicking the icon state which is toggleSearch is updating but it is not updating the initialParams hence it is not working on userMain component as well .

Navigation between screen in react native

So I try to navigate between 2 screens. I created a button on the tab bar(right)
but the problem is that it always says that is undefined.
Here is the code:
static navigationOptions = {
headerTitleStyle: { alignSelf: 'center', },
title:'Oferte',
headerStyle: {
backgroundColor: '#BA272A',
},
headerRight: (
<View style={{paddingRight:11}}>
<Button
color="#ff5c5c" title="Tombola"
onPress={() => this.props.navigation( 'Post', { name: 'Jane'} )}
/>
</View>
),
headerTintColor: 'white',
headerTitleStyle: {
fontWeight: 'bold',
},
}
You can use react-native-router-flux (npm install --save react-native-router-flux)
just make one Navigator.js file and define each page you wanted to navigate.
import React from 'react';
import { Router, Scene } from 'react-native-router-flux';
import LaunchScreen from '../components/LaunchScreen.js';
import Feed from '../components/Feed.js';
const Navigator = () => {
return (
<Router>
<Scene key="root">
<Scene key="lauchscreen" component={LaunchScreen} hideNavBar initial />
<Scene key="feedscreen" type="reset" hideNavBar component={Feed} />
</Scene>
</Router>
);
};
export default Navigator;
now in your App.js file add this:
import Navigator from './src/Navigator.js';
export default class App extends Component<Props> {
render() {
return (
<Navigator />
);
}
}
now in your login.js when you click on login button write this:
import { Actions } from 'react-native-router-flux';
onLoginClick() {
Actions.feedscreen();
}
Thats it.. happy coding.
Looking at https://reactnavigation.org/docs/4.x/redux-integration/#setparams-from-your-screen, probably you should do:
static navigationOptions = ({navigation}) => ({
headerTitleStyle: { alignSelf: 'center', },
title:'Oferte',
headerStyle: {
backgroundColor: '#BA272A',
},
headerRight: (
<View style={{paddingRight:11}}>
<Button
color="#ff5c5c" title="Tombola"
onPress={() => navigation( 'Post', { name: 'Jane'} )}
/>
</View>
),
headerTintColor: 'white',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
When using this inside a static property or method it will refer to the class itself and not the instance of the component.
using react-navigation v5 component base api :
import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
function DetailsScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button title="navigate" onPress={()=> props.navigate("home")} />
</View>
);
}
function HomeScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button title="navigate" onPress={()=> props.navigate("details")} />
</View>
);
}
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
or with old v4 api:
// Other code for HomeScreen here...
class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
</View>
);
}
}
class DetailsScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
</View>
);
}
}
const AppNavigator = createStackNavigator(
{
Home: HomeScreen,
Details: DetailsScreen,
},
{
initialRouteName: 'Home',
}
);
// Other code for App component here...
Inside these components you can asses navigation prop, which consists of helper functions for navigating.Some frequently used one are
naviagtion.navigate("screenName") //navigates to new screen
navigation.goBack() // go backs to previous screen
navigation.push("screen name") // navigates to new screen with stack animation . this only works for stackNavigator
navigation.pop()// navigates to previous screen with stack animation . this only works for stackNavigator

Resources