Why does the header icon load slowly in react-native? - reactjs

I'm using both Stack Navigations and Draw Navigations at the same time.
"react-navigation": "3.0.4"
My App.js
import React, { Component } from "react";
import HomeScreen from "./screen/HomeScreen";
export default class AwesomeApp extends Component {
constructor() {
super();
this.state = {
isReady: false
};
}
async componentWillMount() {
await Expo.Font.loadAsync({
Roboto: require("native-base/Fonts/Roboto.ttf"),
Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf"),
Ionicons: require("native-base/Fonts/Ionicons.ttf")
});
this.setState({ isReady: true });
}
render() {
if (!this.state.isReady) {
return <Expo.AppLoading />;
}
return <HomeScreen />;
}
}
my HomeScreen:
import React, { Component } from "react";
import TestScreen from "../TestScreen";
...
import {
createDrawerNavigator,
createAppContainer,
createStackNavigator
} from "react-navigation";
import { AsyncStorage } from "react-native";
import Expo, { Constants, LocalAuthentication } from "expo";
import TouchID from "react-native-touch-id";
import crypto from "crypto";
import safeCrypto from "react-native-fast-crypto";
import { asyncRandomBytes } from "react-native-secure-randombytes";
const defaultNavigationOptions = {
headerTintColor: "black",
headerStyle: {
borderBottomColor: "#fff",
borderBottomWidth: 1,
shadowColor: "transparent",
elevation: 0
},
headerTitleStyle: {
fontWeight: "bold",
fontSize: 18
}
};
window.randomBytes = asyncRandomBytes;
window.scryptsy = safeCrypto.scrypt;
let pinnumbercheck = AsyncStorage.getItem("pinnumber");
let powersucesscheck = AsyncStorage.getItem("powersucess");
let nicknamecheck = AsyncStorage.getItem("nickname");
let compatible = Expo.LocalAuthentication.hasHardwareAsync();
let fingerprints = Expo.LocalAuthentication.isEnrolledAsync();
const Drawers = createDrawerNavigator(
{
FirstAgree: {
screen: UserAcceptanceScreen
},
Test: { screen: TestScreen }
},
{
initialRouteName: "FirstAgree",
contentComponent: props => <SideBar {...props} />
}
);
const SettingStack = createStackNavigator(
{
screen: SettingScreen
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const FirstAgreeStack = createStackNavigator(
{
screen: UserAcceptanceScreen
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const FirstAgreeStack2 = createStackNavigator(
{
screen: UserAcceptanceScreen2
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const WalletScreenStack = createStackNavigator(
{
screen: RegisterWalletScreen2
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const WalletScreen2Stack = createStackNavigator(
{
screen: RegisterWalletScreen3
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const TakeWalletStack = createStackNavigator(
{
screen: TakeWalletScreen
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const RegisterSecurityStack = createStackNavigator(
{
screen: RegisterSecurityScreen
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const RegisterSecurityStack2 = createStackNavigator(
{
screen: RegisterSecurityScreen2
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const PinNumberLoginStack = createStackNavigator(
{
screen: PinNumberLogin
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const TestssStack = createStackNavigator(
{
screen: Testss
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const NickRegisterStack = createStackNavigator(
{
screen: NickRegisterScreen
},
{
defaultNavigationOptions,
headerLayoutPreset: "center"
}
);
const stackScreen = createStackNavigator(
{
Drawers: {
screen: Drawers
},
UserRight: {
screen: UserRightScreen
},
...(very more)
},
{
initialRouteName: "RegisterWalletIndex",
headerMode: "none"
}
);
const HomeScreenRouter = createAppContainer(stackScreen);
export default HomeScreenRouter;
There's nothing wrong with moving between screens and how to use them.
However, the header icon is displayed too late on the next screen when you move the screen.
The header icon is displayed later than the full screen. So you can't act on the screen right away.
usepage.js:
import React from "react";
import {
View,
Text,
TouchableOpacity,
Alert,
Image,
Platform,
ActivityIndicator
} from "react-native";
import { ListItem, CheckBox, Body } from "native-base";
import styles from "./styles.js";
import { Ionicons } from "#expo/vector-icons";
import { NavigationActions } from "react-navigation";
class UserAcceptanceScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
allCheckbox: false,
checkbox1: false,
checkbox2: false,
checkbox3: false,
loading: false
};
}
static navigationOptions = ({ navigation }) => {
return {
headerLeft: (
<TouchableOpacity
style={{ paddingLeft: 15 }}
onPress={() => navigation.dispatch(NavigationActions.back())}
>
<Ionicons name={"ios-arrow-back"} size={35} color={"black"} />
</TouchableOpacity>
)
};
};
componentDidMount() {
this.setState({
loading: true
});
}
allToggleSwitch() {
this.setState({
allCheckbox: !this.state.allCheckbox
});
}
toggleSwitch1() {
this.setState({
checkbox1: !this.state.checkbox1
});
}
toggleSwitch2() {
this.setState({
checkbox2: !this.state.checkbox2
});
}
toggleSwitch3() {
this.setState({
checkbox3: !this.state.checkbox3
});
}
render() {
return this.state.loading === false ? (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<ActivityIndicator size="large" />
</View>
) : (
<View style={styles.container}>
...
</View>
);
}
}
export default UserAcceptanceScreen;
I don't know why because I did what I wanted. Is there something I'm missing?
Please give us a lot of feedback and help.

I didn't know why it was loading slowly, but I solved this problem by designing one loading screen.
My use loading Screen Page:
import React, { Component } from "react";
import {
AsyncStorage,
Text,
View,
ActivityIndicator,
TouchableOpacity
} from "react-native";
import { Ionicons } from "#expo/vector-icons";
class StartScreen extends Component {
constructor(props) {
super(props);
this.state = {
isReady: false
};
}
static navigationOptions = ({ navigation }) => {
return {
headerLeft: (
<TouchableOpacity
style={{ paddingLeft: 15 }}
onPress={() => navigation.navigate("FirstAgreeStack")}
>
<Ionicons name={"ios-arrow-back"} size={35} color={"#ffffff"} />
</TouchableOpacity>
),
headerRight: null
};
};
async componentWillMount() {
let powersucess = await AsyncStorage.getItem("powersucess");
let keystoredata = await AsyncStorage.getItem("keystoredata");
let nickname = await AsyncStorage.getItem("nickname");
let pinnumber = await AsyncStorage.getItem("pinnumber");
let useTouchId = await AsyncStorage.getItem("useTouchId");
powersucess === null
? this.props.navigation.navigate("CusterMizingAlert")
: keystoredata === null
? this.props.navigation.navigate("RegisterWalletIndex")
: nickname === null
? this.props.navigation.navigate("RegisterWalletIndex")
: pinnumber === null
? this.props.navigation.navigate("RegisterSecurity")
: pinnumber === null
? this.props.navigation.navigate("RealPinNumberLogin")
: useTouchId === "useTouchId"
? this.props.navigation.navigate("TouchIdLogin")
: this.props.navigation.navigate("FaceIdLogin"),
this.setState({ isReady: true });
}
render() {
return this.state.isReady === false ? (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<ActivityIndicator size="large" />
</View>
) : (
<View>
<Text>dfdff</Text>
</View>
);
}
}
export default StartScreen;

Related

In react-native scrollToOffset in flat list is not working properly?

In flatlist scroll to offset is not working properly when the no of items are less than 3. If the no of items are greater or equal to 3 than it is working exactly fine.
import React, { Component } from 'react'
import { Animated, Dimensions, RefreshControl, StyleSheet, View, TouchableOpacity } from 'react-native'
import { HEADER_HEIGHT } from '.'
import { CustomText, FlatListWrapper, FlatListWrapperProps } from '../../../common-library/components'
import { ScrollValueContext } from './Context'
import { get } from 'lodash'
import { CENTER, colors, dimens, ROW, SPACE_BETWEEN } from '../../../common-library/config'
import { TAB_BAR_HEIGHT } from './CommunityHeaderComponent'
import { IconButtonWrapper } from '../generic'
import { icons } from '../../common'
import { log } from '../../config'
const labels = {
GO_TO_TOP: 'Go to Top'
}
const styles = StyleSheet.create({
contentStyle: {
paddingTop: HEADER_HEIGHT
},
scrollToTopContainer: {
position: 'absolute',
top: TAB_BAR_HEIGHT,
alignItems: 'center',
left: 0,
right: 0,
zIndex: 999
},
buttonsContainer: {
borderColor: colors.DuckBlue,
backgroundColor: colors.DuckBlue,
borderRadius: dimens.size25,
width: 110,
flexDirection: ROW,
justifyContent: SPACE_BETWEEN,
paddingVertical: dimens.size10,
alignItems: CENTER
},
buttonCta: {
paddingLeft: dimens.size15
},
goToTopLabel: {
color: colors.White,
textAlign: CENTER
},
crossIconCta: {
justifyContent: CENTER,
alignItems: CENTER,
paddingRight: dimens.size15
// paddingTop: dimens.size2
}
})
interface State {
shouldRefresh?: boolean
showScrollToTopView?: boolean
dontShowGoToTop?: boolean
}
interface Props extends FlatListWrapperProps {
uniqueKey?: string
getFlatListRef?: (ref) => any
getScrolledPosition?: (contentOffset) => any
onPullToRefresh?: () => any
renderScrollToTopView?: boolean
}
export class StickyScrollableFlatlistComponent extends Component<Props, State> {
flatListRef
scrolledValue
timerRef
state = {
shouldRefresh: false,
showScrollToTopView: false,
dontShowGoToTop: false
}
componentDidUpdate(prevProps) {
const { showScrollToTopView, dontShowGoToTop } = this.state
if ((!prevProps.isFetching && this.props.isFetching) || (prevProps.isFetching && !this.props.isFetching)) {
if ((showScrollToTopView || dontShowGoToTop) && this.props.renderScrollToTopView) {
this.setState({
showScrollToTopView: false,
dontShowGoToTop: false
})
}
setTimeout(() => {
log('setTileout is called', this.scrolledValue > HEADER_HEIGHT, this.scrolledValue)
this.flatListRef.scrollToOffset({
offset: this.scrolledValue > HEADER_HEIGHT ? HEADER_HEIGHT : HEADER_HEIGHT,
animated: false
})
}, 2000)
}
}
onRefresh = () => {
const { onPullToRefresh } = this.props
if (onPullToRefresh) {
this.setState({
shouldRefresh: true
})
onPullToRefresh().then(() => {
this.setState({
shouldRefresh: false
})
})
}
}
showScrollToTopView = () => {
const { showScrollToTopView } = this.state
const DEVICE_HEIGHT = Dimensions.get('window').height
if (this.scrolledValue >= 2 * DEVICE_HEIGHT - HEADER_HEIGHT && !showScrollToTopView) {
this.setState({
showScrollToTopView: true
})
} else if (this.scrolledValue <= DEVICE_HEIGHT - HEADER_HEIGHT && showScrollToTopView) {
this.setState({
showScrollToTopView: false
})
}
}
onClikCrossIcon = () => {
this.setState({
dontShowGoToTop: true,
showScrollToTopView: false
})
}
moveToTop = () => {
this.flatListRef.scrollToOffset({
offset: HEADER_HEIGHT,
animated: true
})
}
renderScrollToTopView = () => {
return (
<View style={styles.scrollToTopContainer}>
<View style={styles.buttonsContainer}>
<TouchableOpacity onPress={this.moveToTop} style={styles.buttonCta} activeOpacity={1}>
<CustomText textStyle={styles.goToTopLabel}>{labels.GO_TO_TOP}</CustomText>
</TouchableOpacity>
<TouchableOpacity onPress={this.onClikCrossIcon} style={styles.crossIconCta} activeOpacity={1}>
<IconButtonWrapper iconImage={icons.CROSSWHITE_ICON} iconHeight={dimens.size10} iconWidth={dimens.size10} />
</TouchableOpacity>
</View>
</View>
)
}
render() {
const { shouldRefresh, showScrollToTopView, dontShowGoToTop } = this.state
const { getFlatListRef, uniqueKey, getScrolledPosition, renderScrollToTopView = false } = this.props
return (
<ScrollValueContext.Consumer>
{(context) => (
<>
{showScrollToTopView && !dontShowGoToTop && renderScrollToTopView && this.renderScrollToTopView()}
<FlatListWrapper
{...this.props}
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: context.scrollYValue } } }], {
useNativeDriver: true,
listener: ({ nativeEvent }) => {
const yOffsetValue = get(nativeEvent, 'contentOffset.y', 0)
log('Flatlist wrapper on event is called', yOffsetValue)
this.scrolledValue = yOffsetValue
// context.scrollYValue.setValue(this.scrolledValue)
{
renderScrollToTopView && this.showScrollToTopView()
}
if (getScrolledPosition) {
getScrolledPosition(yOffsetValue)
}
}
})}
refreshControl={
<RefreshControl
refreshing={shouldRefresh}
progressViewOffset={HEADER_HEIGHT}
onRefresh={() => {
this.onRefresh()
}}
/>
}
showCustomizedAnimatedFlatList={true}
contentContainerStyle={[styles.contentStyle, this.props.contentContainerStyle]}
scrollEventThrottle={16}
inputRef={(ref) => {
log('inputRefinputRefis called')
if (ref) {
this.flatListRef = ref
if (getFlatListRef) {
getFlatListRef(ref)
}
context.addFlatListRef(this.flatListRef, uniqueKey)
}
}}
onMomentumScrollEnd={({ nativeEvent }) => {
const { contentOffset } = nativeEvent
if (contentOffset.y === 0) {
log('inside onMomentumScrollEnd')
context.flatListRef.forEach((item) => {
if (item.key !== uniqueKey) {
item.value.scrollToOffset({
offset: 0,
animated: false
})
}
})
}
}}
/>
</>
)}
</ScrollValueContext.Consumer>
)
}
}
So my code is like this. where flatlist wrapper is just the wrapper of flatlist.
In flatlist scroll to offset is not working properly when the no of items are less than 3. If the no of items are greater or equal to 3 than it is working exactly fine.

Nested tab navigators don't work inside drawer navigator

I'm trying to embed tab navigators within a drawer navigator, but the drawer navigator sometimes stops working. Code: https://github.com/myplaceonline/testreactexpo/tree/drawernestedtabs
To reproduce the problem:
Click the Screen2 tab at the bottom
Open the drawer
Click Home
Open the drawer
Click Screen2
Nothing happens
Is there a better way to do this and keep the drawer and bottom tabs synchronized and avoid the issue where the drawer stops working?
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {
createAppContainer,
createBottomTabNavigator,
createDrawerNavigator,
createStackNavigator,
NavigationActions,
DrawerActions,
} from 'react-navigation';
import { Ionicons } from '#expo/vector-icons';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#ffffff",
alignItems: "center",
justifyContent: "center",
},
});
class HomeScreen extends React.Component {
static navigationOptions = {
title: "Home",
};
render() {
return (
<View style={styles.container}>
<Text>Home</Text>
</View>
);
}
}
class Screen2Screen extends React.Component {
static navigationOptions = {
title: "Screen2",
};
render() {
return (
<View style={styles.container}>
<Text>Screen2</Text>
</View>
);
}
}
const AppScreensTabs = {
Home: HomeScreen,
Screen2: Screen2Screen,
};
const AppScreensTabOptions = {
tabBarOptions: {
showLabel: true,
},
defaultNavigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, horizontal, tintColor }) => {
const { routeName } = navigation.state;
let iconName;
// https://expo.github.io/vector-icons/
if (routeName === "Home") {
iconName = "md-home";
} else if (routeName === "Screen2") {
iconName = "md-beer";
}
return <Ionicons name={iconName} size={25} color={tintColor} />;
},
}),
};
const AppScreens = {
TabHome: createBottomTabNavigator(
AppScreensTabs,
Object.assign(
{ initialRouteName: "Home" },
AppScreensTabOptions,
)
),
TabScreen2: createBottomTabNavigator(
AppScreensTabs,
Object.assign(
{ initialRouteName: "Screen2" },
AppScreensTabOptions,
)
),
};
const AppScreensStackNavigationOptions = {
defaultNavigationOptions: ({ navigation }) => ({
headerLeft: <Ionicons name="md-menu" size={25} onPress={ () => navigation.openDrawer() } style={{ marginLeft: 15 }} />
})
};
const AppDrawer = createAppContainer(createDrawerNavigator({
DrawerHome: {
screen: createStackNavigator(
AppScreens,
Object.assign(
{ initialRouteName: "TabHome" },
AppScreensStackNavigationOptions,
)
),
navigationOptions: {
drawerLabel: "Home",
}
},
DrawerScreen2: {
screen: createStackNavigator(
AppScreens,
Object.assign(
{ initialRouteName: "TabScreen2" },
AppScreensStackNavigationOptions,
)
),
navigationOptions: {
drawerLabel: "Screen2",
}
},
}));
export default class App extends React.Component {
render() {
return (
<AppDrawer />
);
}
}
<div data-snack-id="#git/github.com/myplaceonline/testreactexpo#drawernestedtabs" data-snack-platform="ios" data-snack-preview="true" data-snack-theme="light" style="overflow:hidden;background:#fafafa;border:1px solid rgba(0,0,0,.08);border-radius:4px;height:505px;width:100%"></div>
<script async src="https://snack.expo.io/embed.js"></script>
It seems you're trying to keep drawer state and tab state in sync, but I guess that from a UX perspective, it might make more sense to treat them as separate navigation containers, each with their own navigation hierarchy. Keeping them in sync is not straight-forward using react-navigation and is not a pattern I think people will be familiar with when navigation through your app.

Changing the style of "toggled" elements in a list, react-native

I'm having trouble changing the style of only one element in a list.
Below is my Main class, as well as StationDetails class, which is a component I've created to render the list elements one by one.
There is one line (Line 31) in the StationDetails I cant seem to figure out the problem with. I want to style the component based on whether or not the elements' ID is included in the activeStations list.
Here is the line:
style={activeStations.includes(stations.id) ? pressedStyle : buttonStyle}
Here is my Main class
import React, { Component } from "react"
import axios from "axios"
import { Text, View, ScrollView } from "react-native"
import StationDetails from "./StationDetails"
class Main extends Component {
constructor(props) {
super(props)
this.state = { stations: [], pressStatus: false, activeStations: [] }
this.handleClick = this.handleClick.bind(this)
}
componentWillMount() {
axios
.get("https://api.citybik.es/v2/networks/trondheim-bysykkel")
.then(response =>
this.setState({ stations: response.data.network.stations })
)
}
handleClick() {
this.setState({ pressStatus: !this.state.pressStatus })
}
renderStations() {
return this.state.stations.map(stations => (
<StationDetails
activeStations={this.state.activeStations}
handleClick={this.handleClick}
pressStatus={this.state.pressStatus}
key={stations.id}
stations={stations}
>
{stations.name}
</StationDetails>
))
}
render() {
return (
<ScrollView style={{ flex: 1, marginTop: 20 }}>
{this.renderStations()}
</ScrollView>
)
}
}
export default Main
And here is my StationDetails component.
import React from "react"
import { Text, View } from "react-native"
import Card from "./felles/Card"
import CardSection from "./felles/CardSection"
import Button from "./felles/Button"
const StationDetails = ({
stations,
handleClick,
pressStatus,
activeStations
}) => {
const {
headerTextStyle,
leftPartStyle,
rightPartStyle,
pressedStyle,
buttonStyle
} = styles
return (
<Card style={{ flex: 1, flexDirection: "row" }}>
<CardSection style={leftPartStyle}>
<Text style={headerTextStyle}>
{stations.name}
</Text>
<Text>
Free bikes: {stations.free_bikes}
</Text>
</CardSection>
<CardSection style={rightPartStyle}>
<Button
onPress={() => {
if (!activeStations.includes(stations.id)) {
activeStations.push(stations.id)
} else {
activeStations.splice(activeStations.indexOf(stations.id), 1)
}
}}
style={
activeStations.includes(stations.id) ? pressedStyle : buttonStyle
}
>
Abonner
</Button>
</CardSection>
</Card>
)
}
const styles = {
textStyle: {
fontSize: 14
},
leftPartStyle: {
flex: 3,
flexDirection: "column",
justifyContent: "space-between"
},
rightPartStyle: {
flex: 1
},
pressedStyle: {
backgroundColor: "green"
},
headerTextStyle: {
fontSize: 18
},
thumbnailStyle: {
height: 50,
width: 50
},
buttonStyle: {
backgroundColor: "#fff"
}
}
export default StationDetails
You are trying to set state of Main.activeStations from StationDetails which is not advisable. Few things to keep in mind
Main's activeStations is in it's local component level state.
You shouldn't be trying to mutate that from a child component.
Since you assign mutated activeStations state to Main.activeStations from StationDetails, ReactNative (RN) doesn't find a difference in state in it's reconciliation process so does not re-render StationDetails.
We need RN to re-render StationDetails so that it will show the correct style for the buttons etc.
Documentation on setState
This is how I would do it
Let Main render StationDetails
Get a callback from StationDetails on which station was selected
Let Main take care of mutating it's own internal state (activeStations)
By doing it this way,
StationDetails is responsible only for rendering a list of stations given the props and nothing else. It's a dumb component that renders a list.
Main is responsible for handling it's own internal state
Heres the result :
Main.js class
import React, { Component } from 'react';
import axios from 'axios';
import { ScrollView } from 'react-native';
import StationDetails from './StationDetails';
export default class App extends Component {
constructor(props) {
super(props);
this.onSelectStation = this.onSelectStation.bind(this);
this.state = {
stations: [],
pressStatus: false,
activeStations: []
};
}
componentWillMount() {
axios.get('https://api.citybik.es/v2/networks/trondheim-bysykkel')
.then(response => this.setState({ stations: response.data.network.stations }));
}
onSelectStation(stationKey) {
const { activeStations } = this.state;
const activeStationsEdit = activeStations;
if (!activeStations.includes(stationKey)) {
activeStationsEdit.push(stationKey);
} else {
activeStationsEdit.splice(activeStations.indexOf(stationKey), 1);
}
this.setState({ activeStations: activeStationsEdit });
}
renderStations() {
return this.state.stations.map((stations) =>
<StationDetails
activeStations={this.state.activeStations}
pressStatus={this.state.pressStatus}
key={stations.id}
stations={stations}
stationId={stations.id}
onSelectStation={this.onSelectStation}
>
{stations.name}
</StationDetails>
);
}
render() {
return (
<ScrollView style={{ flex: 1, marginTop: 20 }}>
{this.renderStations()}
</ScrollView>
);
}
}
StationDetails class
import React from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
const StationDetails = ({ stations, activeStations, stationId, onSelectStation }) => {
const { headerTextStyle, leftPartStyle, container, pressedStyle, buttonStyle } = styles;
return (
<View style={container}>
<View style={leftPartStyle}>
<Text style={headerTextStyle}>
{stations.name}
</Text>
<Text>
Free bikes: {stations.free_bikes}
</Text>
</View>
<TouchableOpacity
onPress={() => onSelectStation(stationId)}
style={activeStations.includes(stations.id) ? pressedStyle : buttonStyle}
/>
</View>
);
}
const styles = {
container: {
flex: 1,
flexDirection: 'row',
marginBottom: 10
},
leftPartStyle: {
flex: 3,
flexDirection: 'column',
justifyContent: 'space-between'
},
pressedStyle: {
backgroundColor: 'green',
flex: 1,
},
headerTextStyle: {
fontSize: 18
},
buttonStyle: {
backgroundColor: 'skyblue',
flex: 1,
}
};
export default StationDetails;

undefined is not a function evaluating _this2.props.navigator.push

I am trying to scaffold a simple drawer and navigation in react-native.
As you can see I import the drawer and then I instantiate the Navigator below the Toolbar.
I want to be able to change the route from the AppDrawer but the only thing I get after the button click is
*undefined is not a function (evaluating '_this2.props.navigator.push({ id: 'component5' })') *
Note: I have not attached Component 3 or 5 code because they are simple text renders.
index.android.js
import React, {Component} from 'react';
import {AppRegistry, StyleSheet, Text, View, Navigator, ToolbarAndroid} from 'react-native';
import Component3 from "./app/components/Component3/Component3";
import Component5 from "./app/components/Component5/Component5";
import MyAppDrawer from "./app/components/Miscellaneous/AppDrawer";
import Drawer from 'react-native-drawer';
const drawerStyles = {
drawer: {
shadowColor: "#343477",
shadowOpacity: 0.8,
shadowRadius: 0,
}
}
export default class ReactTest extends Component {
constructor(props, context) {
super(props, context);
this.state = {
drawerType: 'overlay',
openDrawerOffset: 50,
closedDrawerOffset: 0,
panOpenMask: .1,
panCloseMask: .9,
relativeDrag: false,
panThreshold: .25,
tweenHandlerOn: false,
tweenDuration: 350,
tweenEasing: 'linear',
disabled: false,
tweenHandlerPreset: null,
acceptDoubleTap: false,
acceptTap: false,
acceptPan: true,
tapToClose: false,
negotiatePan: false,
rightSide: false,
};
}
openDrawer() {
this.drawer.open()
}
renderScene(route, navigator) {
switch (route.id) {
case 'component2':
return (<Component2 navigator={navigator}/>)
case 'component3':
return (<Component3 navigator={navigator}/>)
case 'component4':
return (<Component4 navigator={navigator}/>)
case 'component5':
return (<Component5 navigator={navigator} title="component5"/>)
case 'component6':
return (<Component6 user={route.user} navigator={navigator} title="component6"/>)
}
}
onActionSelected(position) {
console.log("Settings clicked");
}
onIconClicked(position) {
console.log("App Drawer clicked");
}
render() {
var controlPanel = <MyAppDrawer navigator={navigator} closeDrawer={() => {
this.drawer.close();
}}/>
return (
<View style={styles.containerToolbar}>
<Drawer
ref={c => this.drawer = c}
type={this.state.drawerType}
animation={this.state.animation}
captureGestures={true}
openDrawerOffset={this.state.openDrawerOffset}
closedDrawerOffset={this.state.closedDrawerOffset}
panOpenMask={this.state.panOpenMask}
//panCloseMask={this.state.panCloseMask}
relativeDrag={this.state.relativeDrag}
panThreshold={this.state.panThreshold}
content={controlPanel}
styles={drawerStyles}
disabled={this.state.disabled}
// tweenHandler={this.tweenHandler.bind(this)}
// tweenDuration={this.state.tweenDuration}
// tweenEasing={this.state.tweenEasing}
acceptDoubleTap={this.state.acceptDoubleTap}
acceptTap={this.state.acceptTap}
acceptPan={this.state.acceptPan}
tapToClose={this.state.tapToClose}
negotiatePan={this.state.negotiatePan}
// changeVal={this.state.changeVal}
side={this.state.rightSide ? 'right' : 'left'}
>
<ToolbarAndroid
style={styles.toolbar}
title="MyApp"
// logo={require('./dummy_logo.png')}
navIcon={require("./navigation_icon.png")}
onActionSelected={this.onActionSelected}
onIconClicked={this.openDrawer.bind(this)}
titleColor="black"
actions={[
{title: "Log out", show: "never"}
]}
/>
<Navigator
style={styles.container}
initialRoute={{id: 'component3'}}
renderScene={this.renderScene}/>
</Drawer>
</View>
);
}
}
const styles = StyleSheet.create({
containerToolbar: {
flex: 1,
//justifyContent: 'center',
justifyContent: 'flex-start',
// https://github.com/facebook/react-native/issues/2957#event-417214498
alignItems: 'stretch',
backgroundColor: '#F5FCFF',
},
toolbar: {
backgroundColor: '#e9eaed',
height: 56,
},
});
AppRegistry.registerComponent('ReactTest', () => ReactTest);
AppDrawer.js
import React, {Component} from 'react';
import {View, Text, Button, Navigator} from 'react-native';
import styles from './styles';
export default class AppDrawer extends Component {
constructor() {
super();
}
render() {
return (
<View style={styles.controlPanel}>
<Text style={styles.controlPanelWelcome}>
Control Panel
</Text>
<Button
onPress={() => {
console.log("pressed");
this.props.navigator.push({
id: 'component5',
});
}}
title="Component 5"
/>
</View>
)
}
}
Since you don't have MyAppDrawer inside your renderScene function, you don't have access to the navigator. You would need to add a ref and use that to get the navigator:
Add ref={navigator => this.navigator = navigator} to your Navigator component, then you can do
<MyAppDrawer navigator={this.navigator} closeDrawer={() => {
this.drawer.close();
}}/>

React Native Failed to ImportScripts Error

I'm getting an ImportScripts error which means I'm not importing or exporting something correctly I'm guessing. I've narrowed it down to the import { getPath } from '~/redux/modules/camera' line. But I'm not sure why I get an error. I import connect so I have access to dispatch and then I import the getPath function. What else should I be doing? Thanks!
import React, { PropTypes, Component } from 'react';
import {
Dimensions,
StyleSheet,
Text,
TouchableHighlight,
View
} from 'react-native';
import Camera from 'react-native-camera'
import { connect } from 'react-redux'
import { getPath } from '~/redux/modules/camera'
class NimbusCamera extends Component {
static propTypes = {
navigator: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
}
state = {
camera: {
aspect: Camera.constants.Aspect.fill,
captureTarget: Camera.constants.CaptureTarget.disk,
type: Camera.constants.Type.front,
orientation: Camera.constants.Orientation.auto,
flashMode: Camera.constants.FlashMode.auto,
}
isRecording: false,
timeLeft: 30,
limitReached: false
}
render() {
console.log(this.props)
return (
<View style={styles.container}>
<Camera
ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={this.state.camera.aspect}
type={this.state.camera.type}
captureTarget={this.state.camera.captureTarget}
captureAudio={true}
flashMode={this.state.camera.flashMode}
>
<Text style={styles.capture} onPress={this.startRecording.bind(this)}>[CAPTURE]</Text>
<Text style={styles.capture} onPress={this.stopRecording.bind(this)}>[STOP_RECORDING]</Text>
</Camera>
</View>
);
}
startRecording = () => {
if (this.camera) {
this.camera.capture({mode: Camera.constants.CaptureMode.video})
.then((data) => console.log(data))
.catch(err => console.error(err));
this.setState({
isRecording: true
});
let timerId = setInterval(countdown, 1000);
function countdown() {
if (this.state.timeLeft === 0) {
clearTimeout(timerId);
this.setState({isRecording: false})
} else {
this.setState({timeLeft: this.state.timeLeft--})
}
}
}
}
stopRecording = () => {
if (this.camera) {
this.camera.stopCapture();
this.setState({
isRecording: false
});
}
}
}
export default connect()(NimbusCamera)
const styles = StyleSheet.create({
container: {
flex: 1
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
height: Dimensions.get('window').height,
width: Dimensions.get('window').width
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
color: '#000',
padding: 10,
margin: 40
}
});
Here is my redux camera module.
const GET_PATH = 'GET_PATH'
const CLEAR_PATH = 'CLEAR_PATH'
initialState = {
videoPath: ''
}
export function getPath (path) {
return {
type: GET_PATH,
path
}
}
export function clearPath () {
return {
type: CLEAR_PATH
}
}
export default function camera (state = initialState, action) {
switch (action.type) {
case GET_PATH :
return {
...state,
videoPath: action.path
}
case CLEAR_PATH :
return {
...state,
videoPath: ''
}
default :
return state
}
}
Any log in your packager console? usually it print much more detail error info.
errors like these pops-up also when you make a typo in your code. Then module cannot be properly imported. Check your code for typos first :)

Resources