React Native Tab Navigator: How to let icon overflow tabbar? - javascript

I want to let the logo center in the tarbar, and that is overflow tabbar on the top. How to do it ? .......
...................................................................................................................................................
import React from 'react';
import TabNavigator from 'react-native-tab-navigator';
import {
View,
Text,
StyleSheet,
Image
} from 'react-native';
class App extends React.Component {
constructor(props){
super(props)
this.state={
selectedTab:'首页',
}
}
render() {
return (
<View style={styles.container}>
<TabNavigator tabBarStyle={styles.tabBarStyle} tabBarShadowStyle={styles.tabBarShadowStyle}>
<TabNavigator.Item
selected={this.state.selectedTab === '首页'}
title="首页"
selectedTitleStyle={styles.selectedTitleStyle}
renderIcon={() => <Image style={styles.tabBarIcon} source={require('../../res/images/icons/home.png')}/>}
renderSelectedIcon={() => <Image style={[styles.tabBarSelectedIcon ]} source={require('../../res/images/icons/home.png')}/>}
onPress={() => this.setState({ selectedTab: '首页' })} >
<Text>首页</Text>
</TabNavigator.Item>
<TabNavigator.Item
selected={this.state.selectedTab === '产品'}
title="产品"
selectedTitleStyle={styles.selectedTitleStyle}
renderIcon={() => <Image style={styles.tabBarIcon} source={require('../../res/images/icons/product.png')}/>}
renderSelectedIcon={() => <Image style={[styles.tabBarSelectedIcon ]} source={require('../../res/images/icons/product.png')}/>}
onPress={() => this.setState({ selectedTab: '产品' })} >
<Text>产品</Text>
</TabNavigator.Item>
<TabNavigator.Item
renderIcon={() => <Image style={styles.logoIcon} source={require('../../res/images/icons/logo_tab.png')}/>} >
</TabNavigator.Item>
<TabNavigator.Item
selected={this.state.selectedTab === '活动'}
title="活动"
selectedTitleStyle={styles.selectedTitleStyle}
renderIcon={() => <Image style={styles.tabBarIcon} source={require('../../res/images/icons/activity.png')}/>}
renderSelectedIcon={() => <Image style={[styles.tabBarSelectedIcon ]} source={require('../../res/images/icons/activity.png')}/>}
onPress={() => this.setState({ selectedTab: '活动' })} >
<Text>活动</Text>
</TabNavigator.Item>
<TabNavigator.Item
selected={this.state.selectedTab === '我的'}
title="我的"
selectedTitleStyle={styles.selectedTitleStyle}
renderIcon={() => <Image style={styles.tabBarIcon} source={require('../../res/images/icons/profile.png')}/>}
renderSelectedIcon={() => <Image style={[styles.tabBarSelectedIcon ]} source={require('../../res/images/icons/profile.png')}/>}
onPress={() => this.setState({ selectedTab: '我的' })} >
<Text>我的</Text>
</TabNavigator.Item>
</TabNavigator>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
tabBarStyle: {
backgroundColor: '#fff',
overflow: 'visible',
},
tabBarShadowStyle: {
height: 0,
},
selectedTitleStyle: {
color: '#b42325',
},
logoIcon: {
zIndex: 9999,
position: 'absolute',
top: -50,
left: -25,
width: 60, height: 60,
},
tabBarIcon: {
width: 26, height: 26,
resizeMode: 'contain',
tintColor: '#5f5f5f',
},
tabBarSelectedIcon: {
width: 26, height: 26,
resizeMode: 'contain',
tintColor: '#b42325',
}
});
export default App;
Current like this
And I want to like this..

Use custom navigator to customize your tab view could to the trick, but it will include more effort.
Here is the docs https://reactnavigation.org/docs/navigators/custom
And here is a small example https://github.com/react-community/react-navigation/blob/master/examples/NavigationPlayground/js/CustomTabs.js

Related

Return next page and close current page after loads in React Native

I'm newbie in React-native and I'm confused on how can I pass the parameter to another js file when returning a View, The second problem , The code I used below is when returning home the previous page(login) didn't close or gone and the home didn't view properly. I'm aware using react-nativigation but it's hard for me to implement but is there any other way to direct login when it loads? please need some help for this , here's my code,
Login.js After login from googlesignin when the state loaded it goes to another activity
import { Home } from "../screens"
render() {
return (
<View style={styles.container}>
<GoogleSigninButton
style={{ width: 222, height: 48 }}
size={GoogleSigninButton.Size.Wide}
color={GoogleSigninButton.Color.Dark}
onPress={this.signIn}
/>
{this.state.loaded ?
<View style={{ width: 160, marginTop: 10 }}>
{renderLogin(this)} //return some value
</View>
: <Text>Not signIn</Text>}
</View>
);
}
}
function renderLogin(ts) {
return (
<Home /> // i want to pass value to home js example: 'ts.state.userGoogleInfo.user.photo'
)
}
Whole code in Login.js
import React, { Component } from 'react';
import { View, StyleSheet, ToastAndroid, Button, Text, Image } from "react-native";
import { Home } from "../screens"
import {
GoogleSignin,
GoogleSigninButton,
statusCodes,
} from '#react-native-community/google-signin';
GoogleSignin.configure({
webClientId: '2622122222248-3v21124124124124.apps.googleusercontent.com',
offlineAccess: true, // if you want to access Google API on behalf
});
class Login extends Component {
constructor(props) {
super(props)
this.state = {
userGoogleInfo: {},
loaded: false
}
}
static navigationOptions = {
title: 'Login',
};
signIn = async () => {
try {
console.log("Processing");
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
this.setState({
userGoogleInfo: userInfo,
loaded: true
})
console.log(this.state.userGoogleInfo);
console.log(this.state.userGoogleInfo.user.name)
console.log(this.state.userGoogleInfo.user.email)
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
console.log("e 1");
} else if (error.code === statusCodes.IN_PROGRESS) {
console.log("e 2");
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
console.log("e 3");
} else {
console.log(error.message);
}
}
};
render() {
return (
<View style={styles.container}>
<GoogleSigninButton
style={{ width: 222, height: 48 }}
size={GoogleSigninButton.Size.Wide}
color={GoogleSigninButton.Color.Dark}
onPress={this.signIn}
/>
{this.state.loaded ?
<View style={{ width: 160, marginTop: 10 }}>
{renderLogin(this)}
</View>
: <Text>Not signIn</Text>}
</View>
);
}
}
function renderLogin(ts) {
return (
<Home />
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000000',
padding: 15,
},
buttons: {
width: 20
}
});
export default Login;
Home.js
import React from 'react';
import {
StyleSheet,
View,
Text,
ScrollView,
FlatList,
TouchableOpacity,
Image,
ImageBackground,
LogBox
} from 'react-native';
import { PriceAlert, TransactionHistory } from "../components"
import { dummyData, COLORS, SIZES, FONTS, icons, images} from '../constants';
const Home = ({ navigation }) => {
const [trending, setTrending] = React.useState(dummyData.trendingCurrencies)
const [transactionHistory, setTransactionHistory] = React.useState(dummyData.transactionHistory)
function renderHeader(){
const renderItem = ({item, index}) =>(
<TouchableOpacity
style={{
width:155,
paddingVertical:SIZES.padding,
paddingHorizontal:SIZES.padding,
marginLeft: index == 0 ? SIZES.padding : 0,
marginRight: SIZES.radius,
borderRadius: 15,
backgroundColor: COLORS.white
}}
onPress={() => navigation.navigate("CryptoDetail", {currency:item})}
>
{/* Currency*/}
<View style={{ flexDirection:'row'}}>
<View>
<Image
source={item.image}
resizeMode="cover"
style={{
marginTop: 5,
width: 25,
height: 25
}}
/>
</View>
<View style={{marginLeft: SIZES.base}}>
<Text style={{...FONTS.h3}}>{item.currency}</Text>
<Text style={{ color:COLORS.gray, ...FONTS.body3 }}></Text>
</View>
</View>
{/* value*/}
{/* <View style={{ marginTop:SIZES.radius}}> */}
{/* <Text style={{...FONTS.h6}}>₱{item.amount}</Text> */}
{/* <Text style={{color: item.type =="I" ? COLORS.green : COLORS.red, ...FONTS.h5}}>₱{item.amount}</Text> */}
{/* </View> */}
</TouchableOpacity>
)
return(
<View
style={{
width: "100%",
height: 210,
...styles.shadow
}}
>
<ImageBackground
source={images.banner}
resizeMode="cover"
style={{
flex: 1,
alignItems:'center'
}}
>
{/* Header Bar */}
<View
style={{
marginTop:SIZES.padding *1,
width: "100%",
alignItems: "flex-end",
paddingHorizontal: SIZES.padding
}}
>
<TouchableOpacity
style={{
width: 20,
height: 20,
alignItems: "center",
justifyContent:"center"
}}
onPress={() => console.log("Notification on pressed")}
>
<Image
source={icons.notification_white}
resizeMode="contain"
style={{flex: 1}}
/>
</TouchableOpacity>
</View>
{/* Balance */}
<View
style={{
alignItems: 'center',
justifyContent:'center'
}}
>
<Text style={{ color: COLORS.white, ...FONTS.h3}}>Available Balance</Text>
<Text style={{ marginTop:SIZES.base, color:COLORS.white, ...FONTS.h2}}>₱{dummyData.portfolio.balance}</Text>
<Text style={{color:COLORS.white, ...FONTS.body5}}>{dummyData.portfolio.changes} Last 24 hours</Text>
</View>
{/* Trending */}
<View
style={{
position:'absolute',
bottom: "-30%"
}}
>
<Text style={{ marginLeft:SIZES.padding,
color: COLORS.white, ...FONTS.h3 }}>Dashboard</Text>
<FlatList
contentContainerStyle={{marginTop:SIZES.base}}
data={trending}
renderItem={renderItem}
keyExtractor={item => `${item.id}`}
horizontal
showsHorizontalScrollIndicator={false}
/>
</View>
</ImageBackground>
</View>
)
}
function renderAlert(){
return (
<PriceAlert/>
)
}
function renderNotice(){
return (
<View
style={{
marginTop:SIZES.padding-6,
marginHorizontal: SIZES.padding,
padding: 12,
borderRadius:SIZES.radius,
backgroundColor:COLORS.secondary,
...styles.shadow
}}
>
<Text style={{color:COLORS.white, ...FONTS.h4}}>Announcement:</Text>
<Text style={{marginTop:SIZES.base, color:COLORS.white, ...FONTS.body4, lineHeight:18}}>We offer you an application to guide and track your data.
Learn how to use this application by reading instructions and guide.
</Text>
<TouchableOpacity
style={{
marginTop:SIZES.base
}}
onPress={()=> console.log("Learn More")}
>
<Text style={{ textDecorationLine: 'underline',
color:COLORS.green, ...FONTS.h4}}>Learn more
</Text>
</TouchableOpacity>
</View>
)
}
function renderTransactionHistory(){
return (
<TransactionHistory
customContainerStyle={{ ...styles.shadow}}
history={transactionHistory}
/>
)
}
return (
<ScrollView>
<View style={{ flex:1, paddingBottom:130 }}>
{renderHeader()}
{renderAlert()}
{renderNotice()}
{renderTransactionHistory()}
</View>
</ScrollView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
shadow: {
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 4,
},
shadowOpacity: 0.30,
shadowRadius: 4.65,
elevation: 8,
}
})
export default Home;
Apps.js
import React from 'react';
import { Transaction, TevDetail, Login } from "./screens";
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer } from '#react-navigation/native';
import SplashScreen from 'react-native-splash-screen';
import Tabs from "./navigation/tabs";
const Stack = createStackNavigator();
const App = () => {
React.useEffect(() => {
SplashScreen.hide()
}, [])
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false
}}
initialRouteName={'Login'}
>
<Stack.Screen
name="Home"
component={Tabs}
/>
<Stack.Screen
name="TevDetail"
component={TevDetail}
/>
<Stack.Screen
name="Transaction"
component={Transaction}
/>
<Stack.Screen
name="Login"
component={Login}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default App;

React Native onPress being called automatically withNavigation

I would think that onPress should only work when it is triggered by a touch event. This used to be the case, but when I used withNavigation in a screen not in the StackNavigator, onPress seems to be rendering automatically.
HomeScreen.js
function mapStateToProps(state) {
return { action: state.action };
}
function mapDispatchToProps(dispatch) {
return {
openMenu: () =>
dispatch({
type: "OPEN_MENU",
}),
};
}
class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.notificationVisibility = this.notificationVisibility.bind(this);
this.dashboardVisibility = this.dashboardVisibility.bind(this);
this.state = {
scale: new Animated.Value(1),
opacity: new Animated.Value(1),
user: "",
notificationvisibility: true,
dashboardvisibility: false,
};
}
static navigationOptions = {
headerShown: false,
};
componentDidUpdate() {
this.toggleMenu();
}
async componentDidMount() {
StatusBar.setBarStyle("dark-content", true);
let user = await Auth.currentAuthenticatedUser();
this.setState({ user: user.username });
console.log("logged in!");
}
toggleMenu = () => {
if (this.props.action == "openMenu") {
Animated.timing(this.state.scale, {
toValue: 0.9,
duration: 300,
easing: Easing.in(),
}).start();
Animated.spring(this.state.scale, {
toValue: 0.5,
}).start();
StatusBar.setBarStyle("light-content", true);
}
if (this.props.action == "closeMenu") {
Animated.timing(this.state.scale, {
toValue: 1,
duration: 300,
easing: Easing.in(),
}).start();
Animated.spring(this.state.scale, {
toValue: 1,
}).start();
StatusBar.setBarStyle("dark-content", true);
}
};
notificationVisibility = () => {
console.log("pressed!");
this.setState({
notificationvisibility: true,
dashboardvisibility: false,
});
};
dashboardVisibility = () => {
console.log("pressed!");
this.setState({
notificationvisibility: false,
dashboardvisibility: true,
});
};
render() {
return (
<RootView>
<Menu />
<AnimatedContainer
style={{
transform: [{ scale: this.state.scale }],
opacity: this.state.opacity,
}}
>
<SafeAreaView>
<ScrollView style={{ height: "100%" }}>
<HeaderBox>
<TitleBar>
<Title>Welcome back,</Title>
<Name>{this.state.user}</Name>
<TouchableOpacity
onPress={this.props.openMenu}
style={{ position: "absolute", right: 30 }}
>
<Avatar source={require("../../assets/profilepic.jpg")} />
</TouchableOpacity>
</TitleBar>
<Searchbar>
<Ionicons
name="ios-search"
size={20}
color="#00263A"
style={{ padding: 10 }}
/>
<TextInput />
</Searchbar>
</HeaderBox>
<MenuNav>
<TouchableOpacity onPress={this.notificationVisibility}>
<Navigation>Notifications</Navigation>
</TouchableOpacity>
<TouchableOpacity onPress={this.dashboardVisibility}>
<Navigation>Dashboard</Navigation>
</TouchableOpacity>
</MenuNav>
<ScrollView
style={{
flexDirection: "row",
padding: 20,
paddingLeft: 12,
paddingTop: 65,
paddingBottom: 35,
}}
horizontal={true}
showsHorizontalScrollIndicator={false}
>
{logos.map((logo, index) => (
<Notification
key={index}
image={logo.image}
text={logo.text}
text2={logo.text2}
/>
))}
</ScrollView>
{this.state.dashboardvisibility ? <DashboardHome /> : null}
{this.state.notificationvisibility ? <NotificationHome /> : null}
</ScrollView>
</SafeAreaView>
</AnimatedContainer>
</RootView>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
MenuScreen.js
class Menu extends React.Component {
constructor(props) {
super(props);
this.signOut = this.signOut.bind(this);
}
state = {
top: new Animated.Value(screenHeight),
};
async signOut() {
// const { navigation } = this.props;
await AsyncStorage.getAllKeys().then(AsyncStorage.multiRemove);
Auth.signOut();
this.props.navigation.navigate("AuthLoading");
}
componentDidMount() {
this.toggleMenu();
}
componentDidUpdate() {
this.toggleMenu();
}
toggleMenu = () => {
if (this.props.action == "openMenu") {
Animated.spring(this.state.top, {
toValue: 54,
}).start();
}
if (this.props.action == "closeMenu") {
Animated.spring(this.state.top, {
toValue: screenHeight,
}).start();
}
};
render() {
return (
<AnimatedContainer style={{ top: this.state.top }}>
<Cover>
<Image source={require("../../assets/backgroundimg.png")} />
<Title>Vanessa Wong</Title>
<Subtitle>filler</Subtitle>
</Cover>
<TouchableOpacity
onPress={this.props.closeMenu}
style={{
position: "absolute",
top: 120,
left: "50%",
marginLeft: -22,
zIndex: 1,
}}
>
<CloseView>
<Ionicons name="ios-close" size={44} color="#000000" />
</CloseView>
</TouchableOpacity>
<Content>
<MenuItem icon="ios-settings" title="Account" text="settings" />
<MenuItem icon="ios-card" title="Billing" text="payments" />
<NavigationContainer ref={navigationRef}>
<TouchableOpacity onPress={() => this.signOut()}>
<MenuItem icon="ios-exit" title="Logout" text="see you soon!" />
</TouchableOpacity>
</NavigationContainer>
</Content>
</AnimatedContainer>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(withNavigation(Menu));
Is there something wrong with the way I'm calling onPress={this.props.openMenu} in HomeScreen.js? I have tried onPress={()=>this.props.openMenu} to no avail.
Try to comment those two out:
componentDidMount() {
// this.toggleMenu();
}
componentDidUpdate() {
// this.toggleMenu();
}

Passing data to custom picker

Currently I'm developing an app with a picker on it and I'm using picker component from react native but it doesn't work perfectly on iOS device so I found a custom picker using picker and modal it renders a modal on iOS only.
Here is the code
class PickerWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
type_absen: '',
modal: false,
}
}
render() {
let picker;
let iosPickerModal = (
<Modal isVisible={this.state.modal} hideModalContentWhileAnimating={true} backdropColor={color.white} backdropOpacity={0.9} animationIn="zoomInDown" animationOut="zoomOutUp" animationInTiming={200} animationOutTiming={200} onBackButtonPress={() => this.setState({ modal: false })} onBackdropPress={() => this.setState({ modal: false })} >
<View style={{ backgroundColor: color.white, width: 0.9 * windowWidth(), height: 0.3 * windowHeight(), justifyContent: 'center' }}>
<Picker
selectedValue={this.state.type_absen}
onValueChange={(itemValue, itemIndex) => {
this.setState({ type_absen: itemValue });
this.setState({ modal: false });
setTimeout(() => this.props.onSelect(itemValue), 1200);
}}
>
{this.props.items.map((item, key) => <Picker.Item label={item} value={item} key={key} />)}
</Picker>
</View>
</Modal>);
if (Platform.OS === 'ios')
return (
<View style={this.props.style}>
{iosPickerModal}
<TouchableOpacity onPress={() => this.setState({ modal: true })}>
<View style={{ flexDirection: 'row', height: this.props.height ? this.props.height : normalize(40), width: this.props.width ? this.props.width : 0.68 * windowWidth(), borderWidth: 1, borderColor: color.blue, alignItems: 'center', borderRadius: 5 }}>
<Text style={{ fontSize: fontSize.regular, marginRight: 30 }}> {this.state.type_absen}</Text>
<IconWrapper name='md-arrow-dropdown' type='ionicon' color={color.light_grey} size={20} onPress={() => this.setState({ modal: true })} style={{ position: 'absolute', right: 10 }} />
</View>
</TouchableOpacity>
</View >);
else
return (
<View style={this.props.style} >
<Picker
selectedValue={this.state.type_absen}
style={{ height: this.props.height ? this.props.height : normalize(20), width: this.props.width ? this.props.width : normalize(150) }}
onValueChange={(itemValue, itemIndex) => {
this.setState({ type_absen: itemValue });
this.props.onSelect(itemValue);
}}
>
{this.props.items.map((item, key) => <Picker.Item label={item} value={item} key={key} />)}
</Picker>
</View>);
}
}
PickerWrapper.propTypes = {
onSelect: PropTypes.func.isRequired
}
export default PickerWrapper;
I successfully load the data from api, but I still confused on how to get the value from it, and here is my old code using picker component
<Picker
selectedValue={this.state.type_absen}
style={{backgroundColor:'white'}}
onValueChange={(val) => this.setState({ type_absen: val })}>
{
this.props.schedules ? this.props.schedules.map((item, key) => {
return <Picker.Item value={item.id} label {item.description} key={item.id} />})
:
[]
}
</Picker>
and this is my new code using the PickerWrapper
export const mapStateToProps = state => ({
token: state.authReducer.token,
message: state.authReducer.message,
schedules: state.authReducer.schedules
});
export const mapDispatchToProps = (dispatch) => ({
actionsAuth: bindActionCreators(authAction, dispatch)
});
class Change extends Component {
constructor(){
super();
this.state={
type_absen: [],
}
}
onPickerValueChange=(value, index)=>{
this.setState({type_absen: value},
() => {
Alert.alert("type_absen", this.state.type_absen);
}
);
}
render() {
return (
<View style={styles.container}>
<View style={styles.container}>
<View style={styles.innerContainer}>
<PickerWrapper items={this.props.schedules.map((item) => item.description )} onSelect={this.onPickerValueChange} />
</View>
</View>
</View>
);
}
}
}
what I'm trying to do is get the item.id. How do I do that in my PickerWrapper component?
In the class component change the picker wrapper like this
export const mapStateToProps = state => ({
token: state.authReducer.token,
message: state.authReducer.message,
schedules: state.authReducer.schedules
});
export const mapDispatchToProps = (dispatch) => ({
actionsAuth: bindActionCreators(authAction, dispatch)
});
class Change extends Component {
constructor(){
super();
this.state={
type_absen: [],
}
}
onPickerValueChange=(value, index)=>{
this.setState({type_absen: value},
() => {
Alert.alert("type_absen", this.state.type_absen);
}
);
}
render() {
return (
<View style={styles.container}>
<View style={styles.container}>
<View style={styles.innerContainer}>
<PickerWrapper items={this.props.schedules} onSelect={this.onPickerValueChange.bind(this)} />
</View>
</View>
</View>
);
}
}
and then in your PickerWrapper component change it to be like this
class PickerWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
type_absen: '',
modal: false,
}
}
render() {
let picker;
let iosPickerModal = (
<Modal isVisible={this.state.modal} hideModalContentWhileAnimating={true} backdropColor={color.white} backdropOpacity={0.9} animationIn="zoomInDown" animationOut="zoomOutUp" animationInTiming={200} animationOutTiming={200} onBackButtonPress={() => this.setState({ modal: false })} onBackdropPress={() => this.setState({ modal: false })} >
<View style={{ backgroundColor: color.white, width: 0.9 * windowWidth(), height: 0.3 * windowHeight(), justifyContent: 'center' }}>
<Picker
selectedValue={this.state.type_absen}
onValueChange={(itemValue, itemIndex) => {
this.setState({ type_absen: itemValue });
this.setState({ modal: false });
setTimeout(() => this.props.onSelect(itemValue), 1200);
}}
>
{this.props.items.map((item, key) => <Picker.Item label={item.description} value={item.id} key={key} />)}
</Picker>
</View>
</Modal>);
if (Platform.OS === 'ios')
return (
<View style={this.props.style}>
{iosPickerModal}
<TouchableOpacity onPress={() => this.setState({ modal: true })}>
<View style={{ flexDirection: 'row', height: this.props.height ? this.props.height : normalize(40), width: this.props.width ? this.props.width : 0.68 * windowWidth(), borderWidth: 1, borderColor: color.blue, alignItems: 'center', borderRadius: 5 }}>
<Text style={{ fontSize: fontSize.regular, marginRight: 30 }}> {this.state.type_absen}</Text>
<IconWrapper name='md-arrow-dropdown' type='ionicon' color={color.light_grey} size={20} onPress={() => this.setState({ modal: true })} style={{ position: 'absolute', right: 10 }} />
</View>
</TouchableOpacity>
</View >);
else
return (
<View style={this.props.style} >
<Picker
selectedValue={this.state.type_absen}
style={{ height: this.props.height ? this.props.height : normalize(20), width: this.props.width ? this.props.width : normalize(150) }}
onValueChange={(itemValue, itemIndex) => {
this.setState({ type_absen: itemValue });
this.props.onSelect(itemValue, index);
}}
>
{this.props.items.map((item, key) => <Picker.Item label={item.description} value={item.id} key={key} />)}
</Picker>
</View>);
}
}
PickerWrapper.propTypes = {
onSelect: PropTypes.func.isRequired
}
export default PickerWrapper;

React Native Redux causing whole page to render upon calling function

I have an issue with React Native Redux where I am calling a Redux action from the onChangeText function from inside a TextField. Normally, an update like this would only cause the TextField itself to update, however the whole page is being re-rendered upon each call of the function.
Here is the code for the TextField. The method in question is this.props.updateURL(value)
<TextField label='Server'
value={this.props.profile.url}
labelFontSize={textFont(16)}
tintColor='#B3B2B6'
autoCapitalize="none"
autoCorrect={false}
returnKeyType={'next'}
blurOnSubmit={false}
autoFocus={!this.props.profile.edited}
style={styles.inputStyle}
selectionColor={CUSRSOR_COLOR}
editable={!this.props.profile.edited}
onChangeText={async value => {
this.props.updateURL(value)
}}
renderAccessory={()=>{
return(<TouchableOpacity onPress={this.handleScanPress} >
<Image source={ImgScan} style={{height: 25, width: 25, overflow:'visible', marginBottom:20, marginRight: 10}}/>
</TouchableOpacity>);
}}
>
</TextField>
This is for the Redux methods mapStateToProps and mapDispatchToProps:
const mapStateToProps = (state) => {
return {
profile : state.profileReducer,
deviceUid: state.GlobalReducer.deviceUid,
locationsList: state.LogsReducer.locationList,
errorList: state.ErrorsReducer.failedList,
watermarkText: state.GlobalReducer.watermarkText,
alertPresent: state.LogsReducer.alertPresent
}
};
const mapDispatchToProps = (dispatch) => {
return {
updateURL : (url) => dispatch(updateURL(url)),
updateUserName : (userName) => dispatch(updateUserName(userName)),
updatePassword : (password) => dispatch(updatePassword(password)),
updateDeviceName : (deviceName) => dispatch(updateDeviceName(deviceName)),
allFieldsEntered : (url,userName,password) => dispatch(allFieldsEntered(url,userName,password)),
loginDevice: (url,userName,password,deviceName,deviceId,navigation,existingUser) => dispatch(callLoginApi(url,userName,password,deviceName,deviceId,navigation,existingUser)),
updateFailureMessage: (message) => dispatch(updateFailureMessage(message)),
showLoader: () => dispatch(showLoader()),
hideLoader: () => dispatch(hideLoader()),
updateRegistrationSuccess: (value) => dispatch(updateRegistrationSuccess(value)),
checkForInternet: () => dispatch(checkConnectivity()),
getProfileDetails: async(isToken) => await dispatch(fetchProfileDetails(isToken)),
updateLocationSwitch: (value) => dispatch(updateLocationStatus(value)),
updateIsEdit: (value) => dispatch(updateIsEdit(value)),
storeRegistrationFlagInAsync: () => dispatch(storeRegistrationFlagInAsync()),
isDeviceConnected: (value) => dispatch(isDeviceConnected(value)),
requestLocationPermissions: () => dispatch(requestLocationPermissions()),
passwordChanged: (value) => dispatch(updateIsPasswordChanged(value)),
isEditPage: (value) => dispatch(isEditPage(value)),
getDeviceName: () => dispatch(getDeviceName()),
shouldEnableLocation: (value) => dispatch(shouldEnableLocation(value)),
fetchLocationList: () => dispatch(fetchLocations()),
syncRecords: (url,userName,password,locationList,deviceId,isFromError,shouldPresentErrorMsg) => dispatch(syncFailedRecords(url,userName,password,locationList,deviceId,isFromError,shouldPresentErrorMsg)),
fetchFailedRecords: (shouldShowLoader) => dispatch(fetchFailedRecords(shouldShowLoader)),
updateDeviceAction: (action,deviceId,url) => dispatch(performDeviceAction(action,deviceId,url)),
callLogoutApi: (userName,password) => dispatch(callLogoutApi(userName,password)),
syncDeviceActions: () => dispatch(syncDeviceActions()),
syncPendingUpdates: (url,userName,password,deviceId) => dispatch(syncPendingUpdates(url,userName,password,deviceId)),
fetchPendingLocationsList: () => dispatch(fetchPendingLocationsList()),
showPasswordChangeAlert: (alertFlag,navigation) => dispatch(handleUIForPasswordChange(alertFlag,navigation)),
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Profile);
Here are the reducers:
const initialState = {
url: '',
userName : '',
password : '',
deviceName : '',
resourceCode : '',
isDeviceConnected : false,
allFieldsFilled: false,
token: '',
gpsTrackingInterval : 5,
apiPostingInterval : 30,
failureMessage : '',
registered : false,
edited: false,
editPage: false,
isConnected: true,
shouldEnableLocation: false,
resourceDesc: '',
adminEmail: '',
companyName: ''
}
const profileReducer = (state=initialState,action) => {
switch (action.type) {
case actionTypes.UPDATE_URL :
return {
...state,
url: action.payload
}
...
}
This is the Redux action:
export const updateURL = (url) => {
return {
type: actionTypes.UPDATE_URL,
payload: url
};
};
Any help would be much appreciated.
import React, { Component } from 'react';
import {
StyleSheet, Text, View, TextInput, TouchableOpacity, Image,
KeyboardAvoidingView, ScrollView, SafeAreaView, Linking
} from 'react-native';
import { COLOR_BLACK, COLOR_GRAY_9, COLOR_TRANSPARENT, COLOR_GRAY_4 } from '../../../constants/colors';
import ImgProntoLogo from '../../../assets/images/pronto-logo.png';
import ImgPasswordVisibleIcon from '../../../assets/images/visibility.png';
import ImgUrl from '../../../assets/images/url.png';
import ImgBarcode from '../../../assets/images/scan-barcode.png';
import ImgUsernameIcon from '../../../assets/images/username.png';
import ImgPasswordIcon from '../../../assets/images/password.png';
import ImgPasswordHide from '../../../assets/images/password-hide.png';
import GlobalStyleSheet from '../../../constants/styles';
import {
PASSWORD, USERNAME, URL_CONNECT
} from '../../../constants/strings';
import { StackActions, NavigationActions } from 'react-navigation';
import Sync from '../../../utils/Syncing';
import { AsyncStorage } from 'react-native';
export default class Configuration extends Component {
constructor(props) {
super(props)
this.handleOpenURL = this.handleOpenURL.bind(this);
this.handleScanPress = this.handleScanPress.bind(this);
}
async componentWillMount() {
this.props.clearConfiguration();
this.props.checkConnectivity();
Linking.addEventListener('url', this.handleOpenURL);
Sync.removeDataFromUserTable();
Sync.removeDataFromCompanyTable();
}
componentDidMount() {
console.log("COMPONENT MOUNTING");
Linking.getInitialURL().then((url) => {
if (url) {
url = url.substring(18);
this.props.updateURL(url);
}
}).catch(err => console.error('An error occurred', err))
}
componentWillReceiveProps(props) {
console.log("WILL RECEIVE")
}
handleOpenURL = (event) => {
this.props.updateURL(event.url.substring(18))
}
copyRights() {
var currentYear = new Date().getFullYear().toString();
return '© '.concat(currentYear).concat(' Pronto Software Limited.All Rights Reserved.');
}
// Connect the device to the pronto connect
async connectDevice() {
this.props.showLoader();
this.props.resetConnectPasswordVisibility();
await this.props.connectDevice(this.props.configuration.url,
this.props.configuration.username, this.props.configuration.password);
let isDeviceConnected = await AsyncStorage.getItem("isConnected");
if (isDeviceConnected === "true") {
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Register' })],
});
this.props.navigation.dispatch(resetAction);
}
this.props.hideLoader();
}
static navigationOptions = {
header: null
}
handleScanPress() {
const action = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Scanner' })],
});
this.props.navigation.push('Scanner');
}
render() {
return (
<SafeAreaView style={GlobalStyleSheet.container}>
<KeyboardAvoidingView style={GlobalStyleSheet.container} >
<View style={styles.headerLogoView}>
<Image source={ImgProntoLogo} style={styles.logo} />
</View>
<ScrollView keyboardShouldPersistTaps="handled" style={styles.scrollViewStyle}>
<View style={{ flex: 1, height: '100%' }}>
<View style={styles.viewContainer}>
<Text style={[GlobalStyleSheet.textHeading4, GlobalStyleSheet.marginTop10,
GlobalStyleSheet.marginBottom10]}>CONFIGURE DEVICE</Text>
<View style={[GlobalStyleSheet.textInputView, GlobalStyleSheet.marginBottom10]}>
<Image style={GlobalStyleSheet.iconInputBar} source={ImgUrl} />
<TextInput
underlineColorAndroid={COLOR_TRANSPARENT}
style={GlobalStyleSheet.textInput}
autoCapitalize='none'
placeholder={URL_CONNECT}
value={this.props.configuration.url}
onChangeText={(value) => { this.props.updateURL(value) }}
editable={!(this.props.configuration.isDeviceConnected)}
/>
<TouchableOpacity style={[GlobalStyleSheet.passwordTouchable]} onPress={this.handleScanPress}>
<Image style={[GlobalStyleSheet.passwordShowHideIcon]} source={ImgBarcode} />
</TouchableOpacity>
</View>
<View style={[GlobalStyleSheet.textInputView, GlobalStyleSheet.marginBottom10]}>
<Image style={GlobalStyleSheet.iconInputBar} source={ImgUsernameIcon} />
<View style={[GlobalStyleSheet.flexRow, { flex: 1 }]}>
<TextInput
placeholder={USERNAME}
underlineColorAndroid={COLOR_TRANSPARENT}
style={GlobalStyleSheet.textInput}
onChangeText={(value) => { this.props.updateUsername(value) }}
editable={!(this.props.configuration.isDeviceConnected)}
/>
</View>
</View>
<View style={[GlobalStyleSheet.textInputView, GlobalStyleSheet.marginBottom10]} >
<Image style={[GlobalStyleSheet.iconInputBar]} source={ImgPasswordIcon} />
<View style={[GlobalStyleSheet.flexRow, { flex: 1 }]}>
<TextInput
placeholder={PASSWORD}
underlineColorAndroid={COLOR_TRANSPARENT}
contextMenuHidden={true}
style={GlobalStyleSheet.textInput}
autoCapitalize='none'
onChangeText={(value) => { this.props.updatePassword(value) }}
editable={!(this.props.configuration.isDeviceConnected)}
secureTextEntry={!(this.props.configuration.passwordVisibility)} />
<TouchableOpacity style={[GlobalStyleSheet.passwordTouchable]}
onPress={() => {
if (!this.props.configuration.isDeviceConnected) {
this.props.togglePasswordVisibility(this.props.configuration.passwordVisibility)
}
}}>
{
this.props.configuration.passwordVisibility ?
(<Image style={[GlobalStyleSheet.passwordShowHideIcon]} source={ImgPasswordVisibleIcon} />) :
(<Image style={[GlobalStyleSheet.passwordShowHideIcon]} source={ImgPasswordHide} />)
}
</TouchableOpacity>
</View>
</View>
{
this.props.configuration.isDeviceConnected ?
(
<TouchableOpacity style={GlobalStyleSheet.touchableBarProcessed} disabled={true} >
<Text style={GlobalStyleSheet.textTouchableBarProcessed}>CONNECTED</Text>
</TouchableOpacity>
) :
(this.props.configuration.url != "" &&
this.props.configuration.username != "" && this.props.configuration.password != "" ?
(
<TouchableOpacity style={[GlobalStyleSheet.touchableBarEnabled]}
onPress={async () => { await this.connectDevice() }}>
<Text style={GlobalStyleSheet.textTouchableBarEnabled}>CONNECT</Text>
</TouchableOpacity>
) :
(
<TouchableOpacity style={[GlobalStyleSheet.touchableBarDisabled]} disabled={true}>
<Text style={[GlobalStyleSheet.textTouchableBarDisabled]}>CONNECT</Text>
</TouchableOpacity>
)
)
}
</View>
</View>
<View style={styles.copyRightsView}>
<Text style={GlobalStyleSheet.copyRightsText}>{this.copyRights()}</Text>
</View>
</ScrollView>
</KeyboardAvoidingView >
</SafeAreaView >
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
height: '100%',
width: '100%',
backgroundColor: COLOR_GRAY_9,
},
headerLogoView: {
height: 50,
backgroundColor: COLOR_GRAY_9,
elevation: 5,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 2,
shadowOffset: { width: 2, height: 2, },
shadowColor: COLOR_BLACK,
shadowOpacity: 0.1,
},
logo: {
height: 40,
width: '80%',
resizeMode: 'contain',
},
viewContainer: {
marginLeft: 15,
marginRight: 15,
paddingBottom: 10
},
copyRightsView: {
alignItems: 'center',
minHeight: 40,
paddingBottom: 5,
},
scrollViewStyle: {
flex: 1,
height: '100%',
paddingTop: 10
},
});

react-native Swiper is blank inside a modal

i use react-native and react-native-swiper
Which OS ?
Android
Version
Which versions are you using:
react-native-swiper v1.5.13?
react-native v0.51.0
Actual behaviour
I display a Modal with swiper inside it. I get a blank display, only the button of the swiper but not the content
Expected behaviour
To show the content of the swiper inside a modal
How to reproduce it>
I tryed to reproduce the bug with a very simplified version of my code
try it in snack
https://snack.expo.io/rk8rb3ZzM
or my code
import React, { Component } from "react";
import { Text, View, Modal, Dimensions } from "react-native";
import Swiper from "react-native-swiper"; // 1.5.13
import { Button } from "react-native-elements"; // 0.18.5
import "#expo/vector-icons"; // 6.2.1
var width = Dimensions.get("window").width;
var height = Dimensions.get("window").height;
export default class extends Component {
constructor(props) {
super(props);
this.state = {
items:[
{ title: "Hello Swiper", css: thisstyles.slide1 },
{ title: "Beautiful", css: thisstyles.slide2 },
{ title: "simple", css: thisstyles.slide3 },
{ title: "And simple", css: thisstyles.slide3 }
],
modalVisible: false
};
}
componentDidMount() {
}
// affiche / cache la modal avec l'image en plein écran
toggleModalVisible = () => {
this.setState({ modalVisible: !this.state.modalVisible });
};
// vue plein écran avec zoom sans info
renderFullScreen() {
if (!this.state.modalVisible) {
return null;
}
return (
<Modal
animationType={"slide"}
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => this.toggleModalVisible()}
>
<View style={thisstyles.modalFullScreen}>
<Text>I have component and text here</Text>
<Swiper style={thisstyles.wrapper} showsButtons={true}>
{this.state.items.map((item, key) => {
console.log("item", item);
return (
<View key={key} style={item.css}>
<Text style={thisstyles.text}>{item.title}</Text>
</View>
);
})}
</Swiper>
</View>
</Modal>
);
}
render() {
return (
<Swiper style={thisstyles.wrapper} showsButtons={true}>
{this.state.items.map((item, key) => {
console.log("item", item);
return (
<View key={key} style={item.css}>
<Text style={thisstyles.text}>{item.title}</Text>
{this.renderFullScreen()}
<Button
title="modal"
onPress={() => this.toggleModalVisible()}
icon={{ name: "close", type: "font-awesome" }}
/>
</View>
);
})}
</Swiper>
);
}
}
const thisstyles = {
modalFullScreen: {
height: height,
width: width
},
wrapper: {},
slide1: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#9DD6EB"
},
slide2: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#97CAE5"
},
slide3: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#92BBD9"
},
text: {
color: "black",
fontSize: 30,
fontWeight: "bold"
}
};
Steps to reproduce
run the app
click on the button "x modal"
I had to add a delay to get mine to work, but yes it works!
constructor(props) {
super(props);
this.state = { showSwiper: false };
}
componentDidMount() {
// Must use this 100-ms delayed swiper workaround to render on Android properly
setTimeout(() => {
this.setState({showSwiper: true});
}, 100);
}
render() {
var exampleSwiper = (
<Swiper activeDotColor={'white'} loop={false} >
<View style={{width: 100, height: 100, backgroundColor: 'white'}} />
<View style={{width: 100, height: 100, backgroundColor: 'white'}} />
<View style={{width: 100, height: 100, backgroundColor: 'white'}} />
</Swiper>
);
return (
<Modal presentationStyle={'overFullScreen'}>
{this.state.showSwiper ? exampleSwiper : null}
</Modal>
);
}
Create one parent View tag and set its height based on your requirement. For example:
<View style={{height:'50%'}}>
<Swiper {....} />
</View>

Categories