I'm developing a new app with RN and using TabNavigator from react-navigation library, the most basic example of TabNavigator only showing first Tab. I've read somewhere that it could be a bug and could be solved by downgrading react-navigation to 1.0.3 but it didn't work for me. How to solve it?
tab1
tab2
app.js
import React, { Component } from 'react';
import Dashboard from './screens/Dashboard';
import Profile from './screens/Profile';
// import {I18nManager} from 'react-native';
// import { Container, Header, Content, Footer, FooterTab, Button, Icon, Text, Badge, Tab, Tabs } from 'native-base';
import { TabNavigator, TabBarBottom } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
export default TabNavigator({
home: { screen: Dashboard },
profile: { screen: Profile },
nav: { screen: Dashboard },
},
{
navigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, tintColor }) => {
const { routeName } = navigation.state;
let iconName;
if (routeName === 'home') {
iconName = `ios-pulse${focused ? '' : '-outline'}`;
} else if (routeName === 'profile') {
iconName = `ios-person${focused ? '' : '-outline'}`;
}
// You can return any component that you like here! We usually use an
// icon component from react-native-vector-icons
return <Ionicons name={iconName} size={25} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: 'blue',
inactiveTintColor: 'gray',
},
tabBarComponent: TabBarBottom,
lazy: false,
tabBarPosition: 'bottom',
animationEnabled: false,
swipeEnabled: false,
}
);
Dashboard.js
import React, { Component } from 'react';
import { TouchableOpacity,
Title,
Subtitle,
Tile,
Divider,
ImageBackground,
Card,
Image,
View,
Caption,
GridRow,
ListView,
Screen
} from '#shoutem/ui';
// import I18n from 'react-native-i18n';
// import {I18nManager} from 'react-native';
// I18nManager.forceRTL(true);
export default class Dashboard extends Component {
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.state = {
restaurants: [
{
'name': 'برنامه ۳۰ روزه هوازی',
'address': 'چربی سوزی | کاهش وزن',
'image': { 'url': 'https://shoutem.github.io/static/getting-started/restaurant-1.jpg' },
},
{
'name': 'تمرین سینه',
'address': 'افزایش قدرت و حجم عضلات سینه و فرم دهی به آن',
'image': { 'url': 'https://shoutem.github.io/static/getting-started/restaurant-2.jpg' },
},
{
'name': 'تمرین شکم',
'address': 'حاضرید که عضلات شکمتان را ورزیده و تکه کنید؟ حرکاتی که در زیر آمده، راهنمایی است که همیشه برای شما کافی و مفید خواهد بود.',
'image': { 'url': 'https://shoutem.github.io/static/getting-started/restaurant-3.jpg' },
},
{
'name': 'تمرین سینه',
'address': 'افزایش قدرت و حجم عضلات سینه و فرم دهی به آن',
'image': { 'url': 'https://shoutem.github.io/static/getting-started/restaurant-2.jpg' },
},
{
'name': 'تمرین شکم',
'address': 'حاضرید که عضلات شکمتان را ورزیده و تکه کنید؟ حرکاتی که در زیر آمده، راهنمایی است که همیشه برای شما کافی و مفید خواهد بود.',
'image': { 'url': 'https://shoutem.github.io/static/getting-started/restaurant-3.jpg' },
},
{
'name': 'تمرین ران پا',
'address': 'این یک تست است.',
'image': { 'url': 'https://shoutem.github.io/static/getting-started/restaurant-2.jpg' },
},
],
};
}
renderRow(rowData, sectionId, index) {
// rowData contains grouped data for one row,
// so we need to remap it into cells and pass to GridRow
if (index === '0') {
return (
<TouchableOpacity key={index}>
<ImageBackground
styleName="large"
source={{ uri: rowData[0].image.url }}
>
<Tile>
<Title styleName="md-gutter-bottom">{rowData[0].name}</Title>
<Subtitle styleName="sm-gutter-horizontal">{rowData[0].address}</Subtitle>
</Tile>
</ImageBackground>
<Divider styleName="line" />
</TouchableOpacity>
);
}
const cellViews = rowData.map((restaurant, id) => {
return (
<TouchableOpacity key={id} styleName="flexible">
<Card styleName="flexible">
<Image
styleName="medium-wide"
source={{ uri: restaurant.image.url }}
/>
<View styleName="content">
<Subtitle numberOfLines={3}>{restaurant.name}</Subtitle>
<View styleName="horizontal">
<Caption styleName="collapsible" numberOfLines={2}>{restaurant.address}</Caption>
</View>
</View>
</Card>
</TouchableOpacity>
);
});
return (
<GridRow columns={2}>
{cellViews}
</GridRow>
);
}
render() {
const restaurants = this.state.restaurants;
// Group the restaurants into rows with 2 columns, except for the
// first restaurant. The first restaurant is treated as a featured restaurant
let isFirstArticle = true;
const groupedData = GridRow.groupByRows(restaurants, 2, () => {
if (isFirstArticle) {
isFirstArticle = false;
return 2;
}
return 1;
});
return (
<ListView
data={groupedData}
renderRow={this.renderRow}
/>
);
}
}
Profile.js
import React, { Component } from 'react';
import { Container, Header, Content, Form, Item, Input, Label } from 'native-base';
// import I18n from 'react-native-i18n';
// import {I18nManager} from 'react-native';
// I18nManager.forceRTL(true);
export default class Profile extends Component {
render() {
return (
<Container>
<Header />
<Content>
<Form>
<Item floatingLabel>
<Label>نام</Label>
<Input />
</Item>
<Item floatingLabel last>
<Label>قد (سانتیمتر)</Label>
<Input />
</Item>
</Form>
</Content>
</Container>
);
}
}
package.json
"dependencies": {
"#shoutem/ui": "^0.23.4",
"native-base": "^2.4.2",
"react": "16.3.1",
"react-native": "0.55.3",
"react-native-vector-icons": "^4.6.0",
"react-navigation": "^1.0.3"
},
"devDependencies": {
"babel-jest": "22.4.3",
"babel-preset-react-native": "4.0.0",
"eslint": "^4.19.1",
"eslint-plugin-react": "^7.7.0",
"jest": "22.4.3",
"react-test-renderer": "16.3.1"
},
"jest": {
"preset": "react-native"
}
I've already tried latest version of react-navigation so its downgraded version you see in package.json
I've found solution to this, and I'm going to be as specific as I can for all the people out there that might face this!
First of all it's a problem with I18nManager.forceRTL(true);
the moment you use this line of code anywhere on your react-native code that its screen gonna get rendered, and on 2nd reload, the app the layout is being change to RTL, and it does not change even if you comment that line! You'll have to use I18nManager.forceRTL(false); and reload a couple of times to get back to normal ltr setup.
The thing is... some of us really need that RTL layout change like I thought I did! Well guess what react-navigation does not respect that, at least for now and in the TabNabigator dept.
So to sum it all up: RTL layout on RN will break your react-navigation's Tab Navigation! (as of the current version you can see in the packages.json above) The issue makes only the first that visible and for the others a bland tab shows, if you turn on the animation or swipe other tabs will show but tabs behave in a weird way, meaning the last tab is active when the first one is active and vice versa... middle tabs are never focused by the way.
So you should know you can't use tabbed navigation with RTL layout. I'll update this answer after this issue got a fix!
I have run it using react-native run-ios, and all the tabs display a different screen. If you are referring to the fact that the nav tab does not change when you click on it whilst on the home tab, both the home and nav tabs are using the Dashboard screen.
The following is my package.json file for this project:
{
"name": "a",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react-native-code-push": "1.15.0-beta",
"#babel/core": "^7.0.0-beta.40",
"#shoutem/ui": "^0.23.4",
"eslint": "^3.19.0",
"native-base": "^2.4.2",
"react": "16.3.1",
"react-native": "0.55.3",
"react-native-vector-icons": "^4.6.0",
"react-navigation": "^1.5.11"
},
"devDependencies": {
"babel-jest": "22.4.3",
"babel-preset-react-native": "4.0.0",
"jest": "22.4.3",
"react-test-renderer": "16.3.1"
},
"jest": {
"preset": "react-native"
}
}
Related
When the app loads there is a white background then a flicker before the splash screen is shown. I have removed the splash screen from the app.json file since i am manually loading and hiding the splash screen. (Leaving the splash screen in the app.json file results in the splash screen being shown followed by a white flicker and then the splash scrren is shown again a second time)
App.js
import React from 'react';
import { StyleSheet, View, Image } from 'react-native'
import { MyAuthStack, MyMainDrawer } from './Screens/Navigators'
import firebase from './firebase'
import { AppLoading, SplashScreen } from 'expo';
import { Asset } from 'expo-asset';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
authState: false,
imgUrl: '',
isSplashReady: false,
isAppReady: false,
}
}
_cacheSplashResourcesAsync = async () => {
const gif = require('./assets/splash.png');
return Asset.fromModule(gif).downloadAsync();
}
_cacheResourcesAsync = async () => {
SplashScreen.hide();
const images = [
require('./assets/addthreadicon.png'),
require('./assets/500x500.png'),
];
const cacheImages = images.map((image) => Asset.fromModule(image).downloadAsync());
await Promise.all(cacheImages);
firebase.auth().onAuthStateChanged(user => {
if (user != null) {
const userRef = firebase.database().ref(`users/${firebase.auth().currentUser.uid}/img`)
userRef.once('value', snapshot => {
this.setState({ imgUrl: snapshot.val(), authState: true, isAppReady: true })
})
} else {
this.setState({ imgUrl: '', authState: false, isAppReady: true })
}
})
};
render() {
const { isSplashReady, isAppReady, authState } = this.state;
if (!isSplashReady) {
return (
<AppLoading
startAsync={this._cacheSplashResourcesAsync}
onFinish={() => this.setState({ isSplashReady: true })}
onError={process.env.NODE_ENV === 'production' ? undefined : console.warn /* eslint-disable-line no-console */}
autoHideSplash={false}
/>
);
}
return (
<View style={{ flex: 1 }}>
{!isAppReady ? (
<Image
source={require('./assets/splash.png')}
onLoad={this._cacheResourcesAsync}
style={{ width: '100%', height: '100%' }}
/>
) : (
<View style={{ flex: 1 }}>
{authState ? (<MyMainDrawer imgUrl={this.state.imgUrl} />) : (<MyAuthStack />)}
</View>
)
}
</View>
)
}
}
});
app.json
{
"expo": {
"name": "Blank Template",
"slug": "movie",
"privacy": "public",
"sdkVersion": "36.0.0",
"platforms": [
"ios",
"android",
"web"
],
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"android": {
"package": "com.saim.moviethreads",
"versionCode": 1,
"config": {
"googleMobileAdsAppId": ""
}
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.yourcompany.yourappname",
"buildNumber": "1.0.0"
}
}
}
Splash Screen flickers because you want to change the state of the application as soon as the splash screen loads. I suggest you use AppLoading from expo, this controls the visibility of the splash screen.
<AppLoading
startAsync={*function to load when splash screen starts*}
onFinish={set the state to finished here}
/>
i'm really new to react native and i encountered this error:
Components\MovieFocusCard.tsx: Assert fail
Failed building JavaScript bundle.
when trying to run the app.
i already tried removing & reinstalling node_modules but it didn't help.
i have no clue why this error is showing up and what the error message itself means. So any help is welcome!
Thanks in advance!
this is the error that shows up on the phone:
package.json:
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"#expo/vector-icons": "^10.0.0",
"babel-preset-react-native": "^4.0.0",
"expo": "^35.0.0",
"react": "16.8.3",
"react-dom": "16.8.3",
"react-native": "https://github.com/expo/react-native/archive/sdk-35.0.0.tar.gz",
"react-native-gesture-handler": "~1.3.0",
"react-native-reanimated": "~1.2.0",
"react-native-screens": "~1.0.0-alpha.23",
"react-native-paper": "^3.3.0",
"react-native-web": "^0.11.7",
"react-navigation": "^4.0.10",
"react-navigation-material-bottom-tabs": "^2.1.5",
"react-navigation-stack": "^1.10.3"
},
"devDependencies": {
"#types/react": "^16.9.14",
"#types/react-native": "^0.57.65",
"#types/react-navigation": "^3.4.0",
"babel-preset-expo": "^7.1.0",
"typescript": "^3.6.3"
},
"private": true
}
Below the code from the MovieFocusCard component:
import MovieDetails from "../Types/MovieDetails";
import { NavigationInjectedProps, withNavigation } from "react-navigation";
import React, { Component } from "react";
import { View, Text, Button, Image } from "react-native";
type MovieFocusCardProps = {};
type MovieFocusCardState = {
movieDetails: MovieDetails;
loaded: boolean;
};
class MovieFocusCard extends Component<
MovieFocusCardProps & NavigationInjectedProps,
MovieFocusCardState
> {
constructor(props: MovieFocusCardProps & NavigationInjectedProps) {
super(props);
this.state = {
movieDetails: null,
loaded: false
};
}
componentDidMount() {
this.GetMovieDetails(this.props.navigation.getParam("movieId", 0));
}
GetMovieDetails(id: number) {
let url: string = "https://api.themoviedb.org/3/movie/";
let apiKey: string = "?api_key=396734bc8915c8d1569cb4ff49b59c56";
fetch(url + id + apiKey)
.then(result => result.json())
.then(data =>
this.setState({
movieDetails: data,
loaded: true
})
)
.catch(console.log);
}
render() {
let posterUrl: string =
"https://image.tmdb.org/t/p/w200" + this.state.movieDetails.poster_path;
let rdate = new Date(
this.state.movieDetails.release_date
).toLocaleDateString();
let element;
this.state.loaded
? (element = (
<View>
<Text>{this.state.movieDetails.title}</Text>
<Image source={{ uri: posterUrl }} />
<Text>Release date: {rdate}</Text>
<Text>Summary: {this.state.movieDetails.overview}</Text>
<Text>Duration: {this.state.movieDetails.runtime} min</Text>
<Button
title="back"
onPress={() => {
this.props.navigation.goBack();
}}
>
Back
</Button>
</View>
))
: (element = <Text>Loading</Text>);
return { element };
}
}
export default withNavigation(MovieFocusCard);
//export default MovieFocusCard;
app.tsx code:
import React from "react";
import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import { createMaterialBottomTabNavigator } from "react-navigation-material-bottom-tabs";
import { Ionicons } from "#expo/vector-icons";
import MovieFocusCard from "./Components/MovieFocusCard";
import MovieHome from "./Components/MovieHome";
import HomeScreen from "./Components/HomeScreen";
const MovieHomeStack = createStackNavigator({
Movies: { screen: MovieHome },
MovieFocusCard: { screen: MovieFocusCard, params: { movieId: Number } }
});
const SerieHomeStack = createStackNavigator({});
const HomeStack = createStackNavigator({});
const MenuBarBottom = createAppContainer(
createMaterialBottomTabNavigator(
{
Movies: {
screen: MovieHomeStack,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Ionicons name="md-videocam" size={26} color={tintColor} />
)
}
},
Series: {
screen: SerieHomeStack,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Ionicons name="md-tv" size={26} color={tintColor} />
)
}
},
Home: {
screen: HomeStack,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Ionicons name="md-heart" size={26} color={tintColor} />
)
}
}
},
{
initialRouteName: "Home",
activeColor: "#000000",
inactiveColor: "#9e9e9e",
barStyle: { backgroundColor: "#ffffff" },
shifting: false //only shows label when clicked
}
)
);
export default MenuBarBottom;
When I build a stand alone android app the header left back button disappears, yet it's there if you click on it. It has no issue on the emulator. What could cause this?
I'm not sure when it started because I was relying on the emulator, but I do know that it was working at some point
Here is my app.json
{
"name": "appname",
"displayName": "appname",
"expo": {
"name": "appname",
"version": "1.0.0",
"slug": "appslug",
"orientation": "portrait",
"privacy": "unlisted",
"sdkVersion": "32.0.0",
"description": "",
"platforms": [
"ios",
"android"
],
"icon": "./assets/images/icon.png",
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"enabled": true,
"fallbackToCacheTimeout": 30000,
"checkAutomatically": "ON_LOAD"
},
"ios": {
"buildNumber": "1.0.0",
"icon": "./assets/images/icon.png",
"bundleIdentifier": "my.unique.id"
// "splash": {
// "backgroundColor": "#FFFFFF",
// "resizeMode": "cover",
// "image": "./assets/iphone/Default-667h#2x.png"
// }
},
"android": {
"versionCode": 1,
"icon": "./assets/images/icon.png",
"package": "my.unique.id",
"adaptiveIcon": {
"foregroundImage": "./assets/images/icon.png",
"backgroundColor": "#FFFFFF"
}
// "splash": {
// "backgroundColor": "#FFFFFF",
// "resizeMode": "cover",
// "mdpi": "./assets/android/res/drawable-mdpi/background.9.png", // natural sized image (baseline),
// "hdpi": "./assets/android/res/drawable-hdpi/background.9.png", // scale 1.5x
// "xhdpi": "./assets/android/res/drawable-xhdpi/background.9.png", // scale 2x
// "xxhdpi": "./assets/android/res/drawable-xxhdpi/background.9.png", // scale 3x
// "xxxhdpi": "./assets/android/res/drawable-xxxhdpi/background.9.png" // scale 4x
// }
},
"hooks": {
"postPublish": [
{
"file": "sentry-expo/upload-sourcemaps",
"config": {
"organization": "my.org",
"project": "proj",
"authToken": "************"
}
}
]
},
"primaryColor": "#fefefe"
}
}
And here is my App.js
import React from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { AppLoading, Asset, Font, Icon } from 'expo';
import AppNavigator from './navigation/AppNavigator';
import { Ionicons } from '#expo/vector-icons';
import Sentry from 'sentry-expo';
// Remove this once Sentry is correctly setup.
Sentry.enableInExpoDevelopment = true;
Sentry.config('https://sentry').install();
export default class App extends React.Component {
state = {
isLoadingComplete: false,
};
async componentDidMount() {
await Font.loadAsync({
'Roboto': require('native-base/Fonts/Roboto.ttf'),
'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
...Ionicons.font,
});
}
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AppNavigator />
</View>
);
}
}
_loadResourcesAsync = async () => {
return Promise.all([
Asset.loadAsync([
require('./assets/images/robot-dev.png'),
require('./assets/images/robot-prod.png'),
]),
Font.loadAsync({
// This is the font that we are using for our tab bar
...Icon.Ionicons.font,
// We include SpaceMono because we use it in HomeScreen.js. Feel free
// to remove this if you are not using it in your app
'space-mono': require('./assets/fonts/SpaceMono-Regular.ttf'),
}),
]);
};
_handleLoadingError = error => {
// In this case, you might want to report the error to your error
// reporting service, for example Sentry
console.warn(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
/navigation/MainTabNavigator.js
import React from 'react';
import {Platform} from 'react-native';
import {createBottomTabNavigator, createStackNavigator} from 'react-navigation';
import TabBarIcon from '../components/TabBarIcon';
import HomeScreen from '../screens/HomeScreen';
import NotificationScreen from '../screens/NotificationScreen';
import SettingsScreen from '../screens/SettingsScreen';
import ProfileScreen from "../screens/ProfileScreen";
import DraftScreen from "../screens/DraftScreen";
import StatsScreen from "../screens/StatsScreen";
import Colors from "../constants/Colors";
import ViewStoryScreen from "../screens/ViewStoryScreen";
import LoginScreen from "../screens/LoginScreen";
import RegisterScreen from "../screens/RegisterScreen";
import MyStoriesScreen from "../screens/MyStories";
import EditStoryScreen from "../screens/EditStoryScreen";
import AddStoryScreen from "../screens/AddStoryScreen";
const headerStyle = {
/* The header config from HomeScreen is now here */
/*
For full list of options
https://reactnavigation.org/docs/en/stack-navigator.html#navigationoptions-for-screens-inside-of-the-navigator
*/
defaultNavigationOptions: {
headerStyle: {
backgroundColor: Colors.headerBackgroundColor,
},
headerTintColor: Colors.headerTintColor,
headerTitleStyle: {
fontWeight: 'bold',
},
headerBackTitleStyle: {color: Colors.headerTintColor},
headerBackStyle: {color: Colors.headerTintColor},
headerBackAllowFontScaling: true,
},
};
const HomeStack = createStackNavigator({
Home: HomeScreen,
ViewStoryScreen: ViewStoryScreen,
EditStory: EditStoryScreen,
AddStory: AddStoryScreen,
},
{
/* The header config from HomeScreen is now here */
defaultNavigationOptions: headerStyle.defaultNavigationOptions
}
);
HomeStack.navigationOptions = {
tabBarLabel: 'Home',
tabBarIcon: ({focused}) => (
<TabBarIcon
focused={focused}
name={
Platform.OS === 'ios'
? `ios-home`
: 'md-home'
}
/>
),
};
const NotificationStack = createStackNavigator({
Links: NotificationScreen,
ViewStoryScreen: ViewStoryScreen,
},
{
/* The header config from HomeScreen is now here */
defaultNavigationOptions: headerStyle.defaultNavigationOptions
});
NotificationStack.navigationOptions = {
tabBarLabel: 'Notifications',
tabBarIcon: ({focused}) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? 'ios-notifications' : 'md-notifications'}
/>
),
};
const SettingsStack = createStackNavigator({
Settings: SettingsScreen,
Profile: ProfileScreen,
Drafts: DraftScreen,
Stats: StatsScreen,
Login: LoginScreen,
Register: RegisterScreen,
MyStories: MyStoriesScreen,
ViewStoryScreen: ViewStoryScreen,
EditStory: EditStoryScreen,
AddStory: AddStoryScreen,
},
{
/* The header config from HomeScreen is now here */
defaultNavigationOptions: headerStyle.defaultNavigationOptions
}
);
SettingsStack.navigationOptions = {
tabBarLabel: 'Settings',
tabBarIcon: ({focused}) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? 'ios-options' : 'md-options'}
/>
),
};
export default createBottomTabNavigator({
HomeStack,
NotificationStack,
SettingsStack,
});
Solution
As #Masuk Helal Anik mentioned this is a bug
Here is what worked for me, but had to sacrifice header back title.
In every screen add this
static navigationOptions = ({navigation}) => {
return {
headerLeft: (
<Ionicons
name={Platform.OS === "ios" ? "ios-arrow-back" : "md-arrow-back"}
size={Platform.OS === "ios" ? 35 : 24}
color={Colors.headerTintColor}
style={
Platform.OS === "ios"
? { marginBottom: -4, width: 25, marginLeft: 9 }
: { marginBottom: -4, width: 25, marginLeft: 20 }
}
onPress={() => {
navigation.goBack();
}}
/>
),
title: 'Screen Title'
}
};
It seems to me like a bug. As a solution in this issue is stated
if you use expo you should include the assets from react-navigation in
your assetBundlePatterns so the images are bundled with your app when
you build a standalone app. the easiest way to do this is to just
bundle all assets that your app uses:
https://github.com/expo/new-project-template/blob/6d4c5636de573852dfd2f7715cfa152fd9c84f89/app.json#L20-L22.
to fix it in development mode within expo, you can cache the assets
locally as per this guide. we do this in the navigationplayground
example app, so you can copy that code from here.
There is some workaround also. Try out them to find which one working for you!
I got this issue today morning when I was trying to implement a Fluid Navigation between scenes using react-native-fluid-transitions. After I replace createStackNavigator for createFluidNavigator or FluidNavigator I got the screen issue below:
Invariant Violation: withNavigation can only be used on a view hierarchy of a navigator. The wrapped component is unable to get access to navigation from props or context.
Here are my files
index.js
import React from 'react';
import { Platform } from 'react-native';
import {
SafeAreaView,
createStackNavigator,
} from 'react-navigation';
import screens from '../screens';
import AppDrawer, {
contentOptions as appDrawerContentOptions,
} from '../components/AppDrawer';
import { createFluidNavigator, FluidNavigator } from 'react-navigation-fluid-transitions';
// We already have a status bar from expo
if (Platform.OS === 'android') {
SafeAreaView.setStatusBarHeight(0);
}
const defaultStack = createFluidNavigator (
{
Home: {
screen: screens.CpfCheckScreen
},
Login: {
screen: screens.LoginScreen
},
SelectAssociate: {
screen: screens.SelectAssociateScreen
},
EmailRegister: {
screen: screens.EmailRegisterScreen
},
AssociateSelected: {
screen: screens.AssociatedSelectedScreen
},
Welcome: {
screen: screens.WelcomeScreen
},
Survey: {
screen: screens.SurveyScreen
},
WaitingIndication: {
screen: screens.WaitingIndicationScreen
},
BindingSuccess: {
screen: screens.BindingSuccessScreen
},
AuthenticationSteps: {
screen: screens.AuthenticationStepsScreen
},
ProcessingData: {
screen: screens.ProcessingDataScreen
},
SignaturePicture: {
screen: screens.SignaturePictureScreen
},
// CpfPicture: {
// screen: screens.CpfPictureScreen
// },
ConfirmData: {
screen: screens.ConfirmDataScreen
},
DataValidation: {
screen: screens.DataValidationScreen
},
MembershipTerms: {
screen: screens.MembershipTermsScreen
},
SuccessfullRegistration: {
screen: screens.SuccessfullRegistrationScreen
},
ApprovedRegistration: {
screen: screens.ApprovedRegistrationScreen
},
FailedRegistration: {
screen: screens.FailedRegistrationScreen
},
SecurityQuestion:
{
screen: screens.SecurityQuestionScreen
},
AssociatedLoginScreen: {
screen: screens.AssociatedLoginScreen
},
NewAssociateQuizScreen: {
screen: screens.NewAssociateQuizScreen
},
NewAssociateSuccessScreen: {
screen: screens.NewAssociateSuccessScreen
}
},
{
initialRouteName: 'AssociatedLoginScreen',
headerMode: 'none',
}
)
export default defaultStack;
screens.js
import InitializeScreen from './InitializeScreen';
import LoginScreen from './LoginScreen';
import EmailRegisterScreen from './EmailRegisterScreen';
import CpfCheckScreen from './CpfCheckScreen'
import SelectAssociateScreen from './SelectAssociateScreen'
import AssociatedSelectedScreen from './AssociatedSelectedScreen'
import WelcomeScreen from './WelcomeScreen'
import SurveyScreen from './SurveyScreen'
import WaitingIndicationScreen from './WaitingIndicationScreen'
import BindingSuccessScreen from './BindingSuccessScreen'
import AuthenticationStepsScreen from './AuthenticationStepsScreen'
import SignaturePictureScreen from './SignaturePictureScreen'
import ConfirmDataScreen from './ConfirmDataScreen'
// import RgPictureScreen from './RgPictureScreen'
// import CpfPictureScreen from './CpfPictureScreen'
import ProcessingDataScreen from './ProcessingDataScreen'
import DataValidationScreen from './DataValidationScreen'
import MembershipTermsScreen from './MembershipTermsScreen'
import SuccessfullRegistrationScreen from './SuccessfullRegistrationScreen'
import ApprovedRegistrationScreen from './ApprovedRegistrationScreen'
import FailedRegistrationScreen from './FailedRegistrationScreen'
import SecurityQuestionScreen from './SecurityQuestionScreen'
import AssociatedLoginScreen from './AssociatedLoginScreen'
import NewAssociateQuizScreen from './NewAssociateQuizScreen'
import NewAssociateSuccessScreen from './NewAssociateSuccessScreen'
/*
import ModalScreen from './ModalScreen';
import DetailsRegisterScreen from './DetailsRegisterScreen';
import DashboardScreen from './DashboardScreen';
import TaskDetailsScreen from './TaskDetailsScreen';
import TasksScreen from './TasksScreen';
import EventScreen from './EventScreen';
import TaskStepsScreen from './TaskStepsScreen';
import QRScanScreen from './QRScanScreen';
import TaskSuccessScreen from './TaskSuccessScreen';
import LogoutScreen from './LogoutScreen';
import ProductScreen from './ProductScreen';
import ProductQRScanScreen from './ProductQRScanScreen';
import ProductSuccessScreen from './ProductSuccessScreen';
import ContinueAsScreen from './ContinueAsScreen';
import UserProfileScreen from './UserProfileScreen';
import SocialPageLikeTaskScreen from './SocialPageLikeTaskScreen';
*/
export default {
InitializeScreen,
LoginScreen,
EmailRegisterScreen,
CpfCheckScreen,
SelectAssociateScreen,
AssociatedSelectedScreen,
WelcomeScreen,
SurveyScreen,
WaitingIndicationScreen,
BindingSuccessScreen,
AuthenticationStepsScreen,
SignaturePictureScreen,
// RgPictureScreen,
// CpfPictureScreen,
ConfirmDataScreen,
ProcessingDataScreen,
DataValidationScreen,
MembershipTermsScreen,
SuccessfullRegistrationScreen,
ApprovedRegistrationScreen,
FailedRegistrationScreen,
SecurityQuestionScreen,
AssociatedLoginScreen,
NewAssociateQuizScreen,
NewAssociateSuccessScreen
/*
ModalScreen,
DetailsRegisterScreen,
DashboardScreen,
TaskDetailsScreen,
TasksScreen,
EventScreen,
TaskStepsScreen,
QRScanScreen,
TaskSuccessScreen,
LogoutScreen,
ProductScreen,
ProductQRScanScreen,
ProductSuccessScreen,
ContinueAsScreen,
UserProfileScreen,
SocialPageLikeTaskScreen,*/
};
NewAssociateQuizForm
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { Text } from 'native-base';
import { withFormik } from 'formik';
import { withNavigation } from 'react-navigation';
import yup from 'yup';
import { withApollo } from 'react-apollo';
import Auth from '../lib/auth';
import Button from './Button';
import colors from '../config/colors';
import { moderateScale } from '../config/scaling';
import { Transition } from 'react-navigation-fluid-transitions'
import Carousel, { Pagination } from 'react-native-snap-carousel';
import FormField from './FormField';
import ItemInput from './ItemInput';
import AlertContainer from './AlertContainer';
const enhancer = withFormik({
validationSchema: yup.object().shape({
name: yup
.string()
.required('Este campo é obrigatório'),
}),
mapPropsToValues: props => ({
associate: '',
}),
handleSubmit: (values, { props: { client, navigation }, setSubmitting }) => {
const { associate } = values;
console.log('handling submit')
if (!associate ) {
AlertContainer.show({
message: 'Sócio inexistente',
buttonText: 'Tentar novamente',
});
setSubmitting(false);
return;
}
Auth.loginRequest(associate)
.then(res => {
if (!res || !res.data || !res.data.token) {
// We can't tell much to the user
console.log('not working')
AlertContainer.show({
message: 'Erro interno',
buttonText: 'Tentar novamente',
});
return;
}
const { token } = res.data;
console.log('working')
Auth.login(token, client).then(() => navigation.navigate('App'));
})
.catch(error => {
console.log('error on login request', error);
const { response } = error;
if (response) {
// HTTP Request response
const { status } = response;
let message = 'Erro interno';
if (status === 400) {
message = 'Dados inválidos';
}
if (status === 401) {
message = 'Sócio inválido';
}
AlertContainer.show({
message,
buttonText: 'Tentar novamente',
});
} else {
// Raw error message
const errorString = error.toString();
if (errorString && errorString.indexOf('Network Error') >= 0) {
AlertContainer.show({
message: 'Erro de conexão',
buttonText: 'Tentar novamente',
});
}
}
})
.finally(() => setSubmitting(false));
},
});
const AssociateForm = props => {
const {
values,
touched,
errors,
handleSubmit,
setFieldValue,
setFieldTouched,
navigation,
isSubmitting,
} = props;
const renderItem = () => {
return (
<FormField
validateStatus={getValidateStatus('associate')}
error={getError('associate')}
>
<ItemInput
placeholder="Nome e sobrenome..."
onChangeText={text => {
setFieldValue('associate', text)
}}
onBlur={() => setFieldTouched('associate')}
initialValue={values.associate}
maxLength={32}
/>
</FormField>
)
}
const pagination = () => {
return (
<Pagination
dotsLength={3}
activeDotIndex={0}
containerStyle={{ backgroundColor: 'transparent' }}
dotStyle={{
width: 10,
height: 10,
borderRadius: 5,
marginHorizontal: 8,
backgroundColor: '#a18037'
}}
inactiveDotStyle={{
// Define styles for inactive dots here
}}
inactiveDotOpacity={0.4}
inactiveDotScale={0.6}
/>
);
}
const getValidateStatus = param => {
return errors[param] && touched[param]
? 'error'
: touched[param]
? 'success'
: '';
};
const getError = param => {
return errors[param] && touched[param] ? errors[param] : '';
};
return (
<View style={styles.container}>
<Text style={styles.title}>
Responda o questionário
</Text>
<Text style={styles.subtitle}>
{`Para ser um sócio Fiduc você precisa responder a este questionário e entraremos em contato para uma entrevista`}
</Text>
<View>
{ renderItem() }
{ pagination() }
</View>
<Button
primary
style={styles.submitButton}
onPress={ () => { navigation.navigate('NewAssociateSuccessScreen') }}
disabled={isSubmitting}
loading={isSubmitting}
>
<Text
style={[
styles.submitButtonText,
isSubmitting && { color: colors.secondary_text },
]}
uppercase={false}
>
Receber Indicação
</Text>
</Button>
</View>
);
};
export default withNavigation(withApollo(enhancer(AssociateForm)));
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
},
submitButton: {
height: moderateScale(20),
borderRadius: 0,
justifyContent: 'center',
marginTop: moderateScale(50),
paddingHorizontal: 48,
alignSelf: 'center',
},
submitButtonText: {
color: colors.button_text,
fontSize: moderateScale(18),
textAlign: 'center',
alignSelf: 'center',
},
whiteText: {
color: colors.secondary_text,
},
title: {
color: colors.text,
fontSize: moderateScale(23),
textAlign: 'center',
marginBottom: 8
},
subtitle: {
color: colors.text,
fontSize: moderateScale(12),
textAlign: 'justify',
marginBottom: 30
},
description: {
color: colors.secondary_text,
fontSize: moderateScale(16),
textAlign: 'center',
paddingVertical: moderateScale(10),
},
wrapper: {
}
});
package.json
{
"name": "fidelidade-app",
"version": "0.1.0",
"private": true,
"devDependencies": {
"babel-eslint": "^8.2.3",
"eslint": "^4.19.1",
"eslint-config-react-app": "^2.1.0",
"eslint-config-universe": "git+https://git#github.com/expo/eslint-config-universe",
"eslint-plugin-babel": "^5.1.0",
"eslint-plugin-flowtype": "^2.46.3",
"eslint-plugin-import": "^2.11.0",
"eslint-plugin-react": "^7.8.2",
"jest-expo": "26.0.0",
"prettier": "^1.12.1",
"react-native-scripts": "1.13.2",
"react-test-renderer": "16.3.0-alpha.1"
},
"main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
"scripts": {
"start": "react-native-scripts start",
"eject": "react-native-scripts eject",
"android": "react-native-scripts android",
"ios": "react-native-scripts ios",
"test": "jest",
"postinstall": "patch -p0 < apollo.patch || true",
"lint": "eslint ."
},
"jest": {
"preset": "jest-expo"
},
"dependencies": {
"apollo-boost": "^0.1.4",
"axios": "^0.18.0",
"expo": "^27.0.0",
"formik": "^0.11.11",
"graphql": "^0.13.2",
"graphql-tag": "^2.9.1",
"moment-timezone": "^0.5.17",
"native-base": "^2.4.2",
"react": "16.3.1",
"react-apollo": "^2.1.3",
"react-native": "https://github.com/expo/react-native/archive/sdk-27.0.0.tar.gz",
"react-native-datepicker": "^1.7.2",
"react-native-easy-grid": "^0.1.17",
"react-native-gesture-password": "^0.3.4",
"react-native-maps-directions": "^1.6.0",
"react-native-masked-text": "^1.7.2",
"react-native-modal": "^6.1.0",
"react-native-scrollable-tab-view": "^0.8.0",
"react-native-snap-carousel": "^3.7.3",
"react-native-svg": "^6.5.2",
"react-native-swiper": "^1.5.13",
"react-navigation": "^2.0.1",
"react-navigation-fluid-transitions": "^0.2.6",
"yup": "^0.24.1"
}
}
I want to change an icon of TabBarIOS and wrote the following codes, but become the error of Element type is invalid.
import React from 'react';
import { View, TabBarIOS } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import CommunicationTab from './components/communication_tab';
import WhereaboutsTab from './components/whereabouts_tab';
export default class App extends React.Component {
constructor() {
super();
this.state = {
selectedTab: 'WhereaboutsTab',
};
}
render() {
return (
<TabBarIOS selectedTab={this.state.selectedTab}>
<Icon.TabBarItem
title="whereabouts"
iconName='ios-home-outline'
selectedIconName='ios-home'
selected={this.state.selectedTab === 'WhereaboutsTab'}
onPress={() => {
this.setState({
selectedTab: 'WhereaboutsTab'
});
}}
>
<WhereaboutsTab />
</Icon.TabBarItem>
<Icon.TabBarItem
title="communication"
iconName='ios-settings-outline'
selectedIconName='ios-settings'
selected={this.state.selectedTab === 'CommunicationTab'}
onPress={() => {
this.setState({
selectedTab: 'CommunicationTab'
});
}}
>
<CommunicationTab />
</Icon.TabBarItem>
</TabBarIOS>
);
}
}
When use following codes, do not have any problem.
<Icon name="ios-home" />
It is a project of expo which I made in create-react-native-app, will this be related?
package.json
"dependencies": {
"expo": "^16.0.0",
"moment": "^2.18.1",
"react": "16.0.0-alpha.6",
"react-native": "^0.43.4",
"react-native-action-button": "^2.6.9",
"react-native-md-textinput": "^2.0.4",
"react-native-modal-datetime-picker": "^4.6.0",
"react-native-swipeout": "^2.1.1",
"react-native-vector-icons": "^4.1.1"
}
I think you have a typo there.
Try replacing Icon.TabBarItem to Icon.TabBarItemIOS instead.