createBottomTabNavigator: how do I reload/refresh a tab screen on tab on the tab-icon? - react-native

So I have a TabNavigator with 3 screens.
import React from 'react';
import {TabNavigator,createBottomTabNavigator } from 'react-navigation';
import ActivateScannerPage from '../pages/ActivateScannerPage';
import ScanTicketPage from '../pages/ScanTicketPage';
import HomePage from '../pages/HomePage';
import SwipeList from '../components/SwipeList';
import FontAwesome, { Icons } from 'react-native-fontawesome';
import { Icon } from 'react-native-elements';
export default createBottomTabNavigator (
{
HomeScreen:{
screen:HomePage,
navigationOptions: {
tabBarIcon:()=>
<Icon
name='home'
type='font-awesome'
color='#5bc0de'/>
},
},
AcitvateScannerPage:{
screen:ActivateScannerPage,
navigationOptions: {
tabBarIcon:()=> <Icon
name='qrcode'
type='font-awesome'
color='#5bc0de'/>
},
},
ScanTicketPage:{
screen:ScanTicketPage,
navigationOptions: {
tabBarIcon:()=> <Icon
name='ticket'
type='font-awesome'
color='#5bc0de'/>
},
},
},
{
tabBarOptions: {
activeTintColor: '#5bc0de',
inactiveTintColor :'white',
labelStyle: {
fontSize: 12,
},
style: {
backgroundColor: '#444444'
},
}
}
);
When I click on ActivateScannerPage there will be opened the camera for scanning a QR Code.
import React, { Component } from 'react';
import {
StyleSheet,
View,
} from 'react-native';
import QrCode from '../components/QrCode';
class ActivateScannerPage extends Component {
static navigationOptions = {
title: 'Aktivierung Scanner',
};
constructor (props){
super(props);
}
render(){
return(
<View style={styles.viewContent}>
<QrCode scanner={true} headerText="Aktivieren Sie Ihren Scanner"/>
</View>
);
}
}
const styles = StyleSheet.create({
viewContent:{
flex:1
}
});
export default ActivateScannerPage;
So my problem ist when the app starts and I click on the tab "ActivateScannerPage/Aktivierung Scanner" then it opens the camera and I can scan my codes without a problem. But when I tab to another tabScreen, e.g. back to the home screen and then go back to the AcitivateScannerPage, the view is not refreshed or rendered new. So the camera dont open anymore and I see a black screen.
Is there a way to fix this? Can I reload or rerender the screen by tapping on the tabIcon?
Thanks.
EDIT:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
AsyncStorage,
} from 'react-native';
import QRCodeScanner from 'react-native-qrcode-scanner';
import moment from 'moment';
import { Icon } from 'react-native-elements';
class QrCode extends Component {
static navigationOptions=(props)=>({
title: `${props.navigation.state.params.scannerName}`,
headerTintColor: 'white',
headerStyle: {backgroundColor: '#444444'},
headerTitleStyle: { color: 'white' },
})
constructor(props){
super(props);
this.state={
.....some states.....
}
}
onSuccess(e) {
..... do something..... here I get my data which I use
}
fetchDataScanner(dataMacroID,requestKey,hash) {
......
}
fetchDataTicketCheck(dataMacroID,requestKey,ticketValue){
.....
}
fetchDataTicketValidate(dataMacroID,requestKey,dataMicroID,ticketValue){
.....
}
saveDataScannerActivation=()=>{
.....
}
render() {
return (
<View style={styles.viewContent}>
<View style={{flex:4}}>
<QRCodeScanner
reactivateTimeout={2000}
reactivate={true}
onRead={this.onSuccess.bind(this)}
/>
</View>
</View>
);
}
}
......
export default QrCode;

in screens you designed for your tabs have to do flowing steps:
1: import withNavigationFocus from react-navigation to your class .
2: then export your like this : export default withNavigationFocus(yourclassname)
3: use this code to update or manage your state
shouldComponentUpdate = (nextProps, nextState) => {
if (nextProps.isFocused) {
...
return true;
} else {
...
return false;
}
};

With react-navigation you can detect whenever the ActivateScannerPage is active/tapped.
Add this code in componentDidMount in ActivateScannerPage
this.subs = [
this.props.navigation.addListener('didFocus', () => this.isFocused()),
];
And remove the listener if ActivateScannerPage will unmount
componentWillUnmount() {
this.subs.forEach(sub => sub.remove());
}

Related

How to get value from side menu with custom component based on drawer navigation

I faced with the following problem: there's my side navigation class:
import { createAppContainer, createDrawerNavigator } from 'react-navigation';
import { Dimensions } from 'react-native';
import MainTabNavigator from './MainTabNavigator';
import MapsScreen from '../screens/MapsScreen';
import DetailsScreen from '../components/DetailInfoScreen';
const DrawerNavigator = createDrawerNavigator(
{
Home: {
screen: MainTabNavigator
},
Maps: {
screen: DetailsScreen
}
}, {
contentComponent: SideMenu,
drawerWidth: Dimensions.get('window').width - 120,
}
);
export default createAppContainer(DrawerNavigator);
SideMenu.js:
import React, { Component } from 'react';
import { SafeAreaView, Switch, View } from 'react-native';
import { Text } from 'react-native-elements';
export default class SideMenu extends Component {
constructor(props) {
super(props);
this.state = {
switchPosition: false,
};
}
toggleSwitch = (value) => {
this.setState({ switchPosition: value });
};
render() {
const { switchPosition } = this.state;
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1, padding: 16 }}>
{switchPosition
? <Text>Switch to Celsius</Text>
: <Text>Switch to Kelvin</Text>
}
<Switch
style={{ marginTop: 20 }}
onValueChange={this.toggleSwitch}
value={switchPosition}
/>
</View>
</SafeAreaView>
);
}
}
As you can see, there's a switch in my custom component. And so, can you explain me, how can I get the value of this switch from, for example, DetailInfoScreen? Or there are no ways to do it?
The perfect solution would be to use Redux . You can set value in any component and can get value in any component.
If you do not want to use Redux - Short solution would be Define a Global variable and you can get and set value in any component
Create a Class DataHandler.js
let switchStatus;
function setSwitchStatus(status) {
switchStatus = status;
}
function getSwitchStatus() {
return switchStatus;
}
export default {
setSwitchStatus,
getSwitchStatus
};
Set Value in SideMenu
import { setSwitchStatus } from './DataHandler';
toggleSwitch = (value) => {
this.setState({ switchPosition: value });
DataHandler. setSwitchStatus(value);
};
Get Value in DetailInfoScreen
import { getSwitchStatus } from './DataHandler';
componentDidMount() {
const switchStatus = DataHandler.getSwitchStatus()
}

Bottom Navigation | React Native

I use react-native-paper to implement Bottom Navigation.
I have a BottomNav element. It is located on the main page of the application - Feed Page. When I click on the icon in BottomNav to go to the MapScreen page nothing happens. Help solve the problem.
BottomNav
import React from "react";
import { StyleSheet } from "react-native";
import { BottomNavigation } from "react-native-paper";
import { MapScreen } from "../screens/";
export default class BottomNav extends React.Component {
state = {
index: 0,
routes: [
{ key: "Feed", title: "Feed", icon: "photo-album", color: "#6200ee" },
{ key: "MapScene", title: "MapScene", icon: "inbox", color: "#2962ff" }
]
};
render() {
return (
<BottomNavigation
style={styles.BottomNav}
navigationState={this.state}
onIndexChange={index => this.setState({ index })}
renderScene={BottomNavigation.SceneMap({
Feed: MapScreen,
MapScene: MapScreen
})}
/>
);
}
}
MapScreen
import React from "react";
import { View, Text, StyleSheet } from "react-native";
import { HeaderProfile } from "../uikit/";
export default class MapScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
active: "bookmark-border"
};
}
render() {
return (
<View style={styles.MapScreen}>
<View style={styles.Header}>
<HeaderProfile />
</View>
<Text> Text </Text>
</View>
);
}
}
const styles = StyleSheet.create({
MapScreen: {
backgroundColor: "#1f1f1f",
height: "100%",
flex: 1,
width: "100%"
},
Header: {
marginTop: 24
}
});
export { MapScreen };

undefined is not a function (evaluating '_this2.props.navigate('Exercises', { exerciseName:ex})') React Native Navigation

I have looked at similar questions but none of the solutions seem to be working for me and this has stumped me for 2 days now.
The error seems to be coming from passing this.props.navigate through to exerciseList.js however everything I have tried doesn't work. So any advice would be very much appreciated. The error occurs when I click through exerciseList.js to the individually rendered .
Error message screenshot here
Error Message:
undefined is not a function (evaluating '_this2.props.navigate('Exercises', { exerciseName:ex})')
package.json
"dependencies": {
"prop-types": "^15.6.2",
"react": "16.6.3",
"react-native": "0.58.3",
"react-native-elements": "^1.0.0",
"react-native-gesture-handler": "^1.0.15",
"react-native-vector-icons": "^6.3.0",
"react-navigation": "^3.2.3"
},
router.js:
import React from 'react';
import {
createAppContainer,
createMaterialTopTabNavigator,
createStackNavigator} from 'react-navigation';
import { Icon } from 'react-native-elements';
import Home from '../home'
import ExercisePage from '../exercises/exercise';
import ExerciseList from '../exercise-list/exercise-list'
import CreateExerciseList from '../exercise-list/createListPage';
export const Route = createStackNavigator(
{
Home: { screen: Home },
Exercises: { screen: ExercisePage },
CreateList: { screen: CreateExerciseList },
ExerciseList: { screen: ExerciseList },
},
{
initialRouteName: 'Home'
}
);
const AppContainer = createAppContainer(Route);
export default AppContainer;
home.js:
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View, Button, TouchableNativeFeedback } from 'react-native';
import Record from './exercises/reps';
import ExerciseList from './exercise-list/exercise-list'
import ExerciseListItem from './exercise-list/exerciseListItem'
import CreateExerciseList from './exercise-list/createListPage';
export default class Home extends Component {
render() {
return (
<View style={styles.container}>
<CreateExerciseList navigate={this.props.navigation.navigate}/>
</View>
);
}
}
createListPage.js:
import React, { Component } from 'react'
import { View, Text, Button, StyleSheet, TouchableNativeFeedback, ScrollView } from 'react-native'
import ExerciseListItem from './exerciseListItem';
export default class CreateExerciseList extends Component {
constructor(props) {
super(props)
this.state = {
workoutList: [
{
"name": "Leg Day",
"exercises": [
"Benchpress",
"Squat",
"Lateral extensions",
"Bicep curls",
"Tricep extensions",
"Shrugs"
]
},
{
"name": "Arm Day",
"exercises": [
"Jumping Jacks",
"Hack squats",
"Tricep curls",
"Flying"
]
}
]
}
}
render() {
const navigate = this.props.navigate
return (
<ScrollView>
<View>
<Text style={styles.header}>Create new list:</Text>
</View>
<View >
<Button style={styles.buttonNew} title='Create new list +'></Button>
</View>
<View style={styles.listContainer}>
{this.state.workoutList.map((workout) => {
return <TouchableNativeFeedback navigate={navigate} key={Date.now()} onPress={() => navigate('ExerciseList', {
title: workout.name,
exercises: workout.exercises,
})}>
<View>
<Text style={styles.listItem}>{workout.name}</Text>
</View>
</TouchableNativeFeedback>
})}
</View>
</ScrollView>
)
}
}
exerciseList.js:
import React, { Component } from 'react'
import { Text, View, StyleSheet, ScrollView } from 'react-native'
import ExerciseListItem from './exerciseListItem'
export class ExerciseList extends Component {
constructor(props) {
super(props)
this.state = {
exercises: []
}
}
componentDidMount() {
const { navigation } = this.props;
const title = navigation.getParam('title', 'no title available');
const exercises = navigation.getParam('exercises', 'no exercises found');
this.setState({
title: title,
exercises: exercises,
})
}
render() {
const navigate = this.props.navigate
return (
<View style={styles.scrollView}>
<View>
<Text style={styles.header}>{this.state.title}</Text>
</View>
<ScrollView style={styles.scrollView}>
{this.state.exercises.map((ex) => {
return <ExerciseListItem style={styles.listItem} exerciseName={ex} key={Date.now()} onPress={(ex) => navigate('Exercises', {exerciseName: ex})} />
})}
</ScrollView>
</View>
)
}
}
On your exerciseList.js, try importing this:
import { withNavigation } from 'react-navigation';
What withNavigation does is it provides the navigation prop directly to the component, without the need to pass it through props. You can read more about it here:
https://reactnavigation.org/docs/en/connecting-navigation-prop.html
Also, as Atin Singh mentioned on the comments, you should pass the navigation props like this: navigation = {this.props.navigation} and not as you are passing on your HOC. Then you'll just access the props on the child component like this: this.props.navigation.

Changing tab bar background color causes all screens to re-render

I am using react-navigation as my navigation solution. I need to change the background color of my tab bar on click of a button in the settings screen, however, do so would make all screens re-render, here's a live demo of what's going on:
As you can see, every time I press the Change Tab Bar Background Color button in Settings screen, the color of the tab bar changes, however, at the same time, the app automatically navigates to Login screen for some reason. I am using redux to maintain the current theme, here are the code:
Action creator:
export function switchTheme() {
return { type: 'SWITCH_THEME' };
}
Reducer:
const INITIAL_STATE = {
backgroundColor: 'white'
};
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case 'SWITCH_THEME':
return { backgroundColor: state.backgroundColor === 'white' ? 'black' : 'white' };
default:
return state;
}
}
Settings screen:
import React, { Component } from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import { Icon } from 'react-native-elements';
import { connect } from 'react-redux';
import { switchTheme } from '../actions';
class SettingsScreen extends Component {
static navigationOptions = {
title: 'Settings',
header: null,
tabBarIcon: ({ tintColor }) => {
return (<Icon name='settings' size={30} color={tintColor} />);
}
};
render() {
return (
<View style={{ ... }}>
<TouchableOpacity
onPress={this.props.switchTheme}
style={{ ... }}
>
<Text style={{ ... }}>
Change Tab Bar Background Color
</Text>
</TouchableOpacity>
</View>
);
}
}
export default connect(null, { switchTheme })(SettingsScreen);
Main.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { TabNavigator } from 'react-navigation';
import WelcomeScreen from './screens/WelcomeScreen';
import LoginScreen from './screens/LoginScreen';
import RegisterScreen from './screens/RegisterScreen';
import SettingsScreen from './screens/SettingsScreen';
import MessageScreen from './screens/MessageScreen';
class Main extends Component {
render() {
const MainTabNavigator = TabNavigator({
login: { screen: LoginScreen },
register: { screen: RegisterScreen },
message: { screen: MessageScreen },
setting: { screen: SettingsScreen }
}, {
tabBarOptions: {
style: { backgroundColor: this.props.backgroundColor }
}
});
return (
<MainTabNavigator />
);
}
}
const mapStateToProps = state => {
const { backgroundColor } = state.theme;
return { backgroundColor };
};
export default connect(mapStateToProps, null)(Main);
App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import store from './store';
import Main from './Main';
class App extends Component {
render() {
return (
<Provider store={store}>
<Main />
</Provider>
);
}
}
export default App;
Why does the app navigates to Login screen and what can I do to prevent this from happening?
the backgroundcolor piece of state is changed in the reducer SWITCH_THEME
so I think when it comes to the main.js and pull out the backgroundColor from state.theme the prop backgroundColor gets updated and it re-renders

Disable console log in react navigation

I'm using react navigation for my app development. When i run log-android, it keeps logging something like this.
Navigation Dispatch: Action: {...}, New State: {...}
which is from createNavigationContainer.js line 150.
I've run through github and document said it could be done by by setting onNavigationStateChange={null} on a top-level navigator.
How can i achieve this by setting onNavigationStateChange={null} and where should i set it?
I've try to set like below, but it the page will not be able to redirect to other page.
export default () => {
<App onNavigationStateChange={null} />
}
Below are my app.js code
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View } from 'react-native';
import { StackNavigator,DrawerNavigator } from 'react-navigation';
import DrawerContent from './components/drawer/drawerContent.js';
import News from './components/news/home.js';
const drawNavigation = DrawerNavigator(
{
Home : {
screen : News ,
navigationOptions : {
header : null
}
}
},
{
contentComponent: props => <DrawerContent {...props} />
}
)
const StackNavigation = StackNavigator({
Home : { screen : drawNavigation,
navigationOptions: {
header: null
}
}
});
export default StackNavigation;
This is my drawerContent.js
import React, {Component} from 'react'
import {View,Text, StyleSheet,
TouchableNativeFeedback,
TouchableOpacity,
TouchableHighlight
} from 'react-native'
import { DrawerItems, DrawerView } from 'react-navigation';
import Icon from 'react-native-vector-icons/Octicons';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
class DrawerContent extends Component {
constructor(props){
super(props);
console.log('DrawerContent|testtttttt');
}
render(){
return (
<View style={styles.container}>
<Text>Hi darren</Text>
<TouchableOpacity style={{ marginBottom:5 }} onPress={() => this.props.navigation.navigate('RegistrationScreen') } >
<View style={styles.nonIconButton}>
<Text style={{ color: 'black',fontSize: 13 }} >Sign Up</Text>
</View>
</TouchableOpacity>
<Text>Hi darren</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default DrawerContent;
First, make sure you are using the latest release of react-navigation as the comment noting that the fix was committed is fairly recent.
Based on your code example, to disable logging for all navigation state changes, you would want to replace this code:
export default StackNavigation;
with:
export default () => (
<StackNavigation onNavigationStateChange={null} />
);
as StackNavigation appears to be your root navigator.
React navigation is great, but this logging is really bad. Solution
const AppNavigator = StackNavigator(SomeAppRouteConfigs);
class App extends React.Component {
render() {
return (
<AppNavigator onNavigationStateChange={null} />
);
}
}