I have a TabBar with multiple TabBar.Item components. Each TabBar.Item component has it's own NavigatorIOS.
Here's an example of my code for my TabBar.js
<TabBarIOS>
<TabBarIOS.Item
selected={this.state.selectedTab === "profile"}
systemIcon={"most-viewed"}
onPress={() => this.setTab("profile")}
>
<NavigationBar title="Profile" component={Profile} passProps={{ showFilter: this.state.showFilter }} />
</TabBarIOS.Item>
</TabBarIOS>
Within my NavigationBar.js, I simply render out a NavigatorIOS
<NavigatorIOS
ref="nav"
initialRoute={{ ...this.props }}
style={{
flex: 1
}}
/>
When a User clicks the Filter button, this.state.showFilter is updated in TabBar. It's then passed down into NavigatonBar correctly, the render() function inside of NavigationBar is executed however,
At this point, my component won't re-render the component listed in initialRoute (Profile)
Is there any way to achieve this? When clicking the Filter I need to set an optional variable inside of Profile to hide and show the Filter Modal
I'm not sure whether there is a more elegant solution to this, but I have one that works.
I added the following code inside of NavigationBar:
shouldComponentUpdate(nextProps, nextState) {
this.refs.nav.replace({...nextProps})
return true;
}
This will force a reload of the initialRoute component.
Related
The idea is to create a specific Touchable component acting like a button and have a feeling of pressed and unpressed, and using this component many times on my app but only one can be pressed at a time. If one is touched and then another one is touched, the first one should be unpressed and the second one should be pressed. The idea is not to use Redux to solve this problem.
I'm already handling which component was pressed, and sending through props the actions to the component. But i don't know how to manage all buttons at the same time in a generic way, by that I mean, not creating a variable to each button.
my App:
<View>
<ActivityButton activityTitle={"B1"} submit={() => this.submitHandler("B1")} />
<ActivityButton activityTitle={"B2"} submit={() => this.submitHandler("B2")} />
</View>
my Component (ActivityButton):
this.state={active:false}
<TouchableOpacity style={this.state.active ? styles.buttonPress : styles.button} onPress={this.props.submit}>
<View>
<Text>{this.props.activityTitle}</Text>
</View>
</TouchableOpacity>
I assume what you trying to do something like a Radio button groups? If your buttons only located in single page, you can achieve by using state in MyApp to check which buttons is enabled instead of button itself.
MyApp
constructor(props) {
super(props);
this.state = {
buttonIdThatEnable: "",
};
}
submitHandler = (buttonId) => {
this.setState({
buttonIdThatEnable: buttonId,
});
}
render() {
return (
<View>
<ActivityButton
activityTitle={"B1"}
active={this.state.buttonIdThatEnable === "B1"}
submit={() => this.submitHandler("B1")}
/>
<ActivityButton
activityTitle={"B2"}
active={this.state.buttonIdThatEnable === "B2"}
submit={() => this.submitHandler("B2")}
/>
</View>
)
}
ActivityButton (Use props.active to determine style)
<TouchableOpacity style={this.props.active ? styles.buttonPress : styles.button}
onPress={this.props.submit}>
<View>
<Text>{this.props.activityTitle}</Text>
</View>
</TouchableOpacity>
If your Buttons are located in different components and you do not want to use Redux, you may consider the React Context API
<TouchableWithoutFeedback onPress={()=>this.props.navigation.navigate('Home')} >
{
this.props.type!=='1' ? <Icon2 name="chevron-with-circle-left" size={28}/>
: <Icon name='chevron-left' style={{ color: "#16527e" }} size={35}/>
}
</TouchableWithoutFeedback>
This code is showing an error that this.props.navigation.navigate is not an object . Is that necessary to use button for navigation for using this.props.navigation.navigate? can anyone help ?
No, it's not necessary. You can use whatever function for your onPress. I think that the problem is your navigation object.. is it undefined?
Try with this code and read what it's logged:
<TouchableWithoutFeedback
onPress={()=> console.log("Navigation is :", this.props.navigation}>
{ this.props.type!=='1'?
<Icon2 name="chevron-with-circle-left" size={28}/>
: <Icon name='chevron-left' style={{ color: "#16527e" }}
size={35}/>
}
</TouchableWithoutFeedback>
Ensure that navigation contains the "navigate" function and, if it's necessary, log even "this.props.navigation.navigate".
At the end ensure that react-navigation is configured properly.
Something more: if you are trying to use navigation inside a presentational component make sure that navigation is an object inside the container component and pass navigation down as a prop. If react-navigation opens the page XYZ ensure that navigation.navigate works in XYZ and pass it to other components as a prop.
If XYZ opens from react-navigation routing it should have this.props.navigation. XYZ renders ABC. XYZ should pass this.props.navigation to ABC as a prop:
<ABC navigation = {this.props.navigation}/>
I want to hide the status bar, when modal window is shown.
My setup is as following, but it won't work as expected:
<StatusBar animated={true} hidden={true} translucent={true}>
Use statusBarTranslucent
If your status bar is translucent, you can set statusBarTranslucent to the modal.
Added since React Native 0.62
<Modal {...props} statusBarTranslucent>...</Modal>
Credit: https://github.com/react-native-modal/react-native-modal/issues/50#issuecomment-607535322
This is a known issue and there seems to be no official/React way to fix it yet. You can follow the discussion here:
https://github.com/facebook/react-native/issues/7474
I saw a post in this discussion which proposes a hack to hide it, but I haven't tried it on my project. You can also upvote this trick if it works for you.
<View style={styles.outerContainer}
<View style={styles.container}>
<StatusBar hidden={true}/>
<View style={styles.content}>
</View>
<Modal animation={fade} transparent={true}>
{/*Modal Contents Here*/}
</Modal>
</View>
A more solid fix may be changing the theme of activity in native android code.
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.ReactNative.AppCompat.Light.NoActionBar.FullScreen">
<!-- Customize your theme here. -->
</style>
<style name="AppTheme.Launcher">
<item name="android:windowBackground">#drawable/launch_screen</item>
</style>
</resources>
Credits go to Traviskn and mbashiq who proposed fixes above. I recommend you to subscribe that issue.
According to the documentations, you should be able to hide status bar in both iOS and Android using this
import {StatusBar} from 'react-native';
StatusBar.setHidden(true);
We can use the background of StatusBar to solve this problem easily but may not the best.
<Modal transparent>
{Platform.OS === 'android' ?
<StatusBar backgroundColor="rgba(0,0,0,0.5)"/>
: null
}
<View style={{backgroundColor: 'rgba(0,0,0,0.5)'}}>
// ModalContent here
</View>
</Modal>
Just use the same background and this problem can be solved.
I am actually facing the same issue for some time, I tried many solutions but I didn't get rid of this problem. I also tried to use native Android code to hide the StatusBar for a single component it works in other component but when I use it in modal it just not working. So, at last, I got a solution that works for me. I remove the modal view and replace it with react-navigation to navigate to a specific path and handle the back button using BackHandler component.
i achieve this creating a custom status bar component with a modal prop:
import React from 'react'
import { StatusBar } from 'react-native'
const MyStatusBar = (props) => {
const { backgroundColor } = props
const { barStyle } = props
const { translucent } = props
const { hidden } = props
const { showHideTransition } = props
const { modal } = props;
(modal !== undefined) ? StatusBar.setHidden(true) : StatusBar.setHidden(false)
return (
<StatusBar showHideTransition={showHideTransition} hidden={hidden} translucent={translucent} backgroundColor={backgroundColor} barStyle={barStyle} />
)
}
export default MyStatusBar
inside my base component modal prop is undefined so custom status bar is shown:
<MyStatusBar backgroundColor={theme.colors.primary} barStyle={'light-content'} />
and then calling inside the component who call the modal:
<MyStatusBar modal={modalVisible ? true : undefined} />
I think the root of my problem is the same, but it appeared a little different than how it is described above.
Expected behaviour: When the Modal becomes visible the StatusBar should hide.
const [showModal, setShowModal] = useState(false)
...
<Modal
visible={showModal}
>
<StatusBar hidden={showModal} />
...
Actual bahviour: Sometimes the StatusBardissapears as expected, other times just the StatusBar background color goes away and the actual StatusBar remains.
Workaround: Due to the flickering behaviour I think the problem is a racing condition of the native Android dialog. Therefore, I built a custom Modal component that uses the StatusBar imperative api to make sure the StatusBar hide call is made before the Modal appears. So far the Problem has not reappeared.
Here is the custom Modal component:
const Modal = ({ visible, children, ...rest }) => {
const [modalVisibility, setModalVisibility] = useState(false);
useEffect(() => {
if (visible) {
StatusBar.setHidden(true);
setModalVisibility(true);
} else {
StatusBar.setHidden(false);
setModalVisibility(false);
}
}, [visible]);
return (
<RNModal
visible={modalVisibility}
{...rest}
>
{children}
</RNModal>
);
};
export default Modal;
Hello you can try this
<View style={styles.outerContainer}
<View style={styles.container}>
<StatusBar hidden={true}/>
<View style={styles.content}>
</View>
<Modal animation={fade} transparent={true}>
{/* Contents Here*/}
</Modal>
</View>
<StatusBar backgroundColor={'transparent'} translucent={true} />
I am creating a native react application using native base for the UI (http://nativebase.io/docs/v2.0.0/components#footerTab). I am using the footerTabs component and my code is as follows
render() {
return (
<Container>
<Header backgroundColor="#ECEFF1">
<Button transparent>
<Icon name='ios-menu' style={{color: 'black'}}/>
</Button>
<Title style={{color:'black'}}>Header</Title>
</Header>
<Content>
<Profile/>
</Content>
<Footer backgroundColor="#212121">
<FooterTab>
<Button backgroundColor="#000" >
<Icon name="ios-person" size={30} color="#900"/>
<Text>Profile</Text>
</Button>
<Button>
<Icon name="ios-search"/>
<Text>Search</Text>
</Button>
<Button>
<Icon name="ios-camera"/>
<Text>Camera</Text>
</Button>
<Button>
<Icon name="ios-apps"/>
<Text>Apps</Text>
</Button>
<Button>
<Icon active name="ios-navigate"/>
<Text>Navigate</Text>
</Button>
</FooterTab>
</Footer>
</Container>
);
}
I have created different JS files for different functionalities such as Profiles,Search,Apps ect.. and have imported them as follows.
import Profile from './Profile';
How do I implement the onPress functionality on the buttons of the footer to change the component in the content tag depending on what was selected?
<Content>
<Profile/>
</Content>
For eg: If I pressed the search button I want the profile tag to be replaced with and similarly the same for the other buttons.
Here FooterTab from native base is not persist actual tab functionality like UITabBar in iOS, its only persist for design. What you need to do is, keep footer tag in all of your component with all four buttons and keep active accordingly. For changing view by selecting any button you need to replace current view by selected one using navigator like:
<Button onPress={()=> { this.props.navigator.replace({id:'component name'}) }}>
and in your navigator component you should define all required components in renderScene method on the basis of id value in route payload. If you want actual functionality as TabBar work then you can use this third party module react-native-tab-navigator. Thanks!
Instead of replacing the content, why don't you have each Button navigate to a new page?
Let's say you're on the Profile tab. You could do something like this:
import FooterWrapper from './FooterWrapper'
<Footer>
<FooterWrapper tab='profile' navigator={this.props.navigator} />
</Footer>
And then in your FooterWrapper (I just handled a case of two tabs):
constructor(props) {
super(props)
this.state = {
profileTab: this.props.tab === 'profile',
searchTab: this.props.tab === 'search',
}
}
navToProfilePage() {
this.props.navigator.push({
id: 'profile',
tab: 'profile',
})
}
navToSearchPage() {
this.props.navigator.push({
id: 'search',
tab: 'search',
})
}
render() {
return (
<FooterTab>
<Button active={this.state.profileTab} onPress={() => this.navToProfilePage()}>
Profile
<Icon name='ios-person' size={30} color='#900' />
</Button>
<Button active={this.state.searchTab} onPress={() => this.navToSearchPage()}>
Search
<Icon name='ios-search' />
</Button>
</FooterTab>
)
}
Ok so here is I how got it I used the renderContent method within the content tags to generate views depending on the state change when the button was clicked.
<Content>
{this._renderContent(this.state.selectedTab)}
</Content>
The selectedTab is a state variable whose state is set using this.setState in the onPress method. The renderContent has an if function that checks the selected tab and returns the appropriate view. I also tried the navigation approach but this seemed cleaner.
_renderContent = (Tab: string,) => {
if(this.state.selectedTab==="Profile"){
return (
<Profile/>
);
}
else if(this.state.selectedTab==="Search"){
}
}
I am using TabBarIOS.Item which has three options that each lead to a NavigatorIOS, I want to use the TabBar's so that when you click them it takes you to the first page of the NavigatorIOS as apposed to the last one before the user changed tabs, is that possible?
Thanks, Adam
So the answer is, is that TabBARIOS.item is an object and navigatorIOS is also an object, so you can give them both a ref. so they look like this.
<TabBarIOS.Item
title="partners"
selected={this.state.selectedTab === "Partners"}
icon={require("./App/assets/partnersIcon.png")}
onPress={this.partnersHandleChange.bind(this)} >
<View style={styles.main}>
<NavigatePartners ref="partners"></NavigatePartners>
</View>
</TabBarIOS.Item>
And the navigator looks like this
turn (
<NavigatorIOS
ref="navigator"
style={styles.mainContainer}
initialRoute={{
title: 'Partners',
component: Partners,
backButtonTitle: 'Back',
}}/>
Then you change the onclick to look like this
partnersHandleChange(){
if (this.state.selectedTab == "Partners")
{
this.refs.partners.refs.navigator.popToTop();
}
this.setState({
selectedTab: 'Partners',
})
};