Implementing Footer Tabs in Native React using Native Base - react-native

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"){
}
}

Related

How to implement onPress in menu.item? native-base#3.2.1-rc.1

For some reason there's no nativeBase api or documentation on this. I can not get a menu.item to respond to press/click no matter what I try.
Attempts
putting Text/Button elements from react-native into the menu. item
putting the menu in multiple different screens
creating a separate function instead of just arrow function
import React from "react"
import {
Menu,
HamburgerIcon,
Box,
Center,
NativeBaseProvider,
usePropsResolution,
Text,
Pressable
} from "native-base"
import {Alert, } from "react-native";
import { logout } from "../_redux/_actions/authentication.actions";
export const NavMenu = (props) => {
const menuItems = ['Profile','Sign Out'];
return (
<Box h="80%" w="95%" alignItems="flex-start">
<Menu w="150" top="-85" h="100%"
trigger={(triggerProps) => {
return (
<Pressable accessibilityLabel="More options menu" {...triggerProps}>
<HamburgerIcon color="black" />
</Pressable>
)
}}
>
**<Menu.Item onPress={()=>alert("Alert Title")}>**
Logout
</Menu.Item>
</Menu>
</Box>
)
}
export default () => {
return (
<NativeBaseProvider>
<Center flex={1} px="1">
<NavMenu />
</Center>
</NativeBaseProvider>
)
}
I found workaround around this problem. Would be interested in better solution, but this should work (it is also not very clean solution):
<Menu.Item onPress={()=>alert("Alert Title")}>
<Pressable onPress={()=>alert("Alert Title")}><Text>Logout</Text></Pressable>
</Menu.Item>
Basically what I am doing here is putting onPress to the Menu.Item and then same onPress to the child. You can use this approach for more complicated situations like adding button to the menu like this:
<Menu.Group title="Účet">
<Menu.Item onPress={() => console.log('log')}>
<Button colorScheme="red" onPress={() => console.log('log')} leftIcon={<Icon size="s" as={<MaterialIcons name='logout' />} />}>
Logout
</Button>
</Menu.Item>
</Menu.Group>

Changing segment content onPress

I have segments and I want them to act as tabs. I tried using react-navigation onPress of each segment but it doesn't look nice.
I want the segments to stay fixed at the top and on clicking each one the content should change or call a specific component without reloading or noticing that the screen has changed.
<Segment>
<Button first>
<Text>Puppies</Text>
</Button>
<Button>
<Text>Kittens</Text>
</Button>
<Button last active>
<Text>Cubs</Text>
</Button>
</Segment>
<Content>
<Text>Awesome segment</Text>
</Content>
Segments can be customized as a react-navigation single screen separated into multiple components which can be rendered on demand.
Example
(Using native-base components)
state = {
activePage:1,
}
selectComponent = (activePage) => () => this.setState({activePage})
_renderComponent = () => {
if(this.state.activePage === 1)
return <Component1/> //... Your Component 1 to display
else
return <Component2/> //... Your Component 2 to display
}
render() {
return (
<Container>
<Header>
<Left />
<Body>
<Segment>
<Button active={this.state.activePage === 1}
onPress={this.selectComponent(1)}><Text>Component 1</Text></Button>
<Button active={this.state.activePage === 2}
onPress= {this.selectComponent(2)}><Text>Component 2</Text></Button>
</Segment>
</Body>
<Right/>
</Header>
<Content padder>
{this._renderComponent()}
</Content>
</Container>
)
}
You can customize it to add more components based on the conditions set above
Edit
Supposing you navigate to a particular screen that also contains Segment, the on some item button click you can do
this.props.navigation.navigate('OtherSegmentScreen', {activePage: this.state.activePage})
And on that page receive the props as
const {navigate} = this.props.navigation
const previousActiveSegment = navigate.getParams('activePage', '1') //... 1 is default value that you can set

Adjacent JSX elements must be wrapped in an enclosing tag

I want to have a navigation bar at the bottom and a toolbar at the top of every page in my React-Native app. However, if I create these two elements in the index.ios.js file, I get the error: "Adjacent JSX elements must be wrapped in an enclosing tag". If I put tags surrounding the Navigator and NavBar I just see a blank screen in my app. What should I do? Here is what my render function looks like in index.ios.js
render() {
return (
<Navigator
initialRoute={{name: 'Login'}}
renderScene={this.renderScene}
navigationBar={
<Navigator.NavigationBar
style={ styles.nav }
routeMapper={ NavigationBarRouteMapper } />
}
/>
<NavBar />
);
}
As per React 16, if you wan't to avoid the <View> wrapping,
you can return multiple components from render as an array.
return ([
<Navigator key="navigator" />,
<NavBar key="navbar" />
]);
Or in a stateless ES6 component:
import React from 'react';
const Component = () => [
<Navigator key="navigator" />,
<NavBar key="navbar" />
];
export default Component;
Don't forget the key property as React needs (as for iteration on Array) to be able to discriminate your components.
Edit (Nov. 2018)
Your can also use React.Fragment shorthand:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
When you wrap it in a view make sure you set the flex to 1. My guess is that the view you are giving it has no size and therefore the child elements are inheriting their size from the parent (which is 0)
Wrap both inside a View and give that outer View wrapper a style of flex 1. Example:
<View style={{flex: 1}}>
<Navigator
{. . .}
/>
<NavBar />
</View>

React Native NavigatorIOS won't re-render Component

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.

React native: disable auto clicking on each card when the view is rendered

I got a response from API which i then parse and display some features on the screen using nativebase.io cardlist. When all the results are shown i want each card be clickable and each click leading to the next screen of details depending on a particular card's content. However, when the cardlist is rendered for some reason EVERY card is being clicked without the user interaction. So i'm looking for the solution which would allow a card click only when i touch any particular card.
class Results extends Component{
constructor(props){
super(props);
this.state = {
data: this.props.results
}
}
renderRow(item){
const bars = [ require('../assets/bar0.png'),
require('../assets/bar1.png'),
require('../assets/bar2.png'),
require('../assets/bar3.png')]
var bar = item.weight === -1 ? bars[0] : bars[item.weight]
return (
<CardItem button onPress={console.log("Pressed")} style={{width: 400}}>
<Thumbnail source={bar}/>
<Text>{item.label}</Text>
<Text note style={{textDecorationLine: 'line-through'}}>{item.excluded}</Text>
</CardItem>
)
}
render(){
console.log(this.state.data)
return (
<Container>
<Header>
<Title>symptoma</Title>
</Header>
<Content>
<Card dataArray={this.state.data}
renderRow={(item) =>
this.renderRow(item)
}>
</Card>
</Content>
</Container>
);
}
}
export default Results
replace
onPress={console.log("Pressed")}
with
onPress={() => console.log("Pressed")}