react navigation tab navigator inside static layout - javascript

Can I achieve this layout?
sketch of layout:
the header part is shared across all tabs. it's part of the layout in this screen.
and each tab contains a scrollView.
btw, I have tried defining the tab navigator as a component and using that inside the render method, along with the static header component.
render() {
return (
<StaticHeaderComponent />
<MyTabNavigator />
)
}
that does not work. the tab navigator does not render at all.

Here is a simple working example:
MyTabNavigator.js
import React, { Component } from 'react'
import { View, Text, ScrollView } from 'react-native'
import { TabNavigator } from 'react-navigation'
class FirstTab extends Component {
render() {
return (
<ScrollView>
<Text>first tab</Text>
</ScrollView>
)
}
}
class SecondTab extends Component {
render() {
return (
<ScrollView>
<Text>second tab</Text>
</ScrollView>
)
}
}
const MyNavigator = TabNavigator({
first: { screen: FirstTab },
second: { screen: SecondTab }
},
{
tabBarPosition: 'top'
})
export default MyNavigator
App.js
import React, { Component } from 'react'
import { View } from 'react-native'
import MyTabNavigator from './MyTabNavigator'
export default class App extends Component {
render() {
return (
<View style={{flex: 1}}>
<View // place your StaticHeaderComponent here
style={{height: 100, backgroundColor: 'green'}}
/>
<MyTabNavigator/>
</View>
)
}
}

For react-navigation 3.+ the Common mistakes section of the documentation comes in handy. You can find the documentation and the example here.
Specifically, you need to expose the static router and pass navigation as a prop. You can further customise the tab styles as required.
const TabbyNavigator = createMaterialTopTabNavigator({
Tab: TabScreen,
AnotherTab: AnotherTabScreen
});
class SomeScreen extends React.Component {
static router = TabbyNavigator.router;
render() {
return (
<TabbyNavigator navigation={this.props.navigation} />
);
}
}

Related

React Native Navigation Component Not Rendering

I am using react navigation as per the docs but trying to make my app a bit more modular. I placed the result of createStackNavigator into a separate component..
Navigator.js
import React, { Component } from 'react';
import {createAppContainer} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import Home from './views/Home.js';
import TestComponent from './views/TestComponent.js';
const MainNavigator = createStackNavigator({
Home: {screen: Home},
Test: {screen: TestComponent}
});
export default createAppContainer(MainNavigator);
..and importing this component into my App.js
App.js
import React, { Component } from 'react';
import { View } from 'react-native';
import Header from './Header.js';
import Navigator from './Navigator.js';
import FooterMenu from './FooterMenu.js';
class App extends Component {
render() {
return (
<View>
<Header />
<Navigator />
<FooterMenu />
</View>
);
}
}
export default App;
My index.js is as follows:
import { AppRegistry } from 'react-native';
import App from './components/App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
Im finding that my <Header/> and <FooterMenu/> components are rendering but the <Navigator/> component is not.
I found that if I replace the top-level <View> component with a React fragment, it does render.
render() {
return (
<>
<Header />
<Navigator />
<FooterMenu />
</>
);
}
Although this syntax breaks my editor's (sublime) syntax highlighting. Though if I change the fragment to <React.Fragment> React native throws an exception.
My questions are:
Why does <Navigator/> not render if I wrap it in a <View> component?
Why do I get an error if I use <React.Fragment>?
If you want to create your own Navigator, here's how.
It is possible to take an existing Navigator and extend its behavior, using the following approach:
const MyStack = createStackNavigator({ ... });
class CustomNavigator extends React.Component {
static router = MyStack.router;
render() {
const { navigation } = this.props;
return <MyStack navigation={navigation} />;
}
}
Now it is possible to render additional things, observe the navigation prop, and override behavior of the router:
const MyStack = createStackNavigator({ ... });
class CustomNavigator extends React.Component {
static router = {
...MyStack.router,
getStateForAction: (action, lastState) => {
// check for custom actions and return a different navigation state.
return MyStack.router.getStateForAction(action, lastState);
},
};
componentDidUpdate(lastProps) {
// Navigation state has changed from lastProps.navigation.state to this.props.navigation.state
}
render() {
const { navigation } = this.props;
return (
<View>
<Header />
<MyStack navigation={navigation} />
<FooterMenu />
</View>
);
}
}
If you want to know more about this,

Why can't I type in my react-native SearchBar?

When I type, nothing shows up in the search bar, but it knows that I'm typing (from the print statements in my updateSearch function). From my understanding of the react-native searchBar, there isn't even anything I need to do for text to be showing up there, so I really have no idea how I could have screwed this up. This is a part of a larger project.. but I'm praying this issue doesn't have anything to do with the rest of it.
import React, { Component } from "react";
import {
View,
Text,
FlatList,
ActivityIndicator,
SafeAreaView,
StyleSheet
} from "react-native";
import Header from "../components/Header";
import { SearchBar, List, ListItem } from 'react-native-elements';
export default class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
query: "",
data: []
};
}
renderHeader = () => {
return (
<SearchBar
placeholder="Type Here..."
onChangeText={this.updateSearch}
value={this.state.query}
lightTheme={true}
/>
);
}
updateSearch = text => {
console.log("text", text);
const formattedSearch = text.toLowerCase();
this.setState({ query: formattedSearch });
console.log("text", this.state.query);
};
render() {
return (
<SafeAreaView style={styles.container}>
<Header text={"Search"} />
<FlatList
ListHeaderComponent={this.renderHeader}
/>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Expo.Constants.statusBarHeight
}
});
If you want to update your UI with render, you have to update your state.
In your FlatList component, you don't update state. So it never renders again.
You have to use state in your FlatList.
render() {
return (
<SafeAreaView style={styles.container}>
<Header text={"Search"} />
<FlatList
ListHeaderComponent={this.renderHeader}
AnyProp={this.state.anyState}
/>
</SafeAreaView>
);
}
The problem is with the ListHeaderComponent.
I did a demo project to see what was happening :
import React, { Component } from 'react'
import { SafeAreaView, FlatList, TextInput } from 'react-native'
export default class Test extends Component {
constructor(props){
super(props)
this.state={value:""}
}
renderHeader = () =>{
console.log("rendering")
return (<TextInput style={{backgroundColor:"green"}}value={this.state.value} placeholder={"Placeholder"}onChangeText={(text)=> this.setState({value : text})}></TextInput>)
}
render() {
console.log(this.state.value) //Logs the value
return (
<SafeAreaView style={{flex:1}}>
<FlatList
ListHeaderComponent={this.renderHeader}
/>
</SafeAreaView>
)
}
}
It looks like once the component has rendered, it does not update anymore. The log inside the render does actually update, but the component do not re-render himself (probably cause the FlatList do not update the ListHeaderComponent once it is done with the first render)
I would suggest to move the SearchBar above the FlatList and enclose everything inside a ScrollView.
EDIT,
To confirm that it was actually updating, i created another TextInput and put it outside the FlatList, and it worked normally:
import React, { Component } from 'react'
import { SafeAreaView, FlatList, TextInput } from 'react-native'
export default class Test extends Component {
constructor(props){
super(props)
this.state={value:""}
}
renderHeader = () =>{
console.log("rendering")
return (<TextInput style={{backgroundColor:"green"}}value={this.state.value} placeholder={"Placeholder"}onChangeText={(text)=> this.setState({value : text})}></TextInput>)
}
render() {
console.log(this.state.value) //Logs the value
return (
<SafeAreaView style={{flex:1}}>
<TextInput style={{backgroundColor:"red"}}value={this.state.value} placeholder={"Placeholder"}onChangeText={(text)=> this.setState({value : text})}></TextInput>
<FlatList
ListHeaderComponent={this.renderHeader}
/>
</SafeAreaView>
)
}
}

React Native - Navigation error while import custom component

I'm trying to make custom component here (footer). Here's my code:
Footer.js
import React, { Component } from 'react'
import {
View,
Text,
Linking,
StyleSheet,
Image,
TouchableOpacity
Dimensions,
} from 'react-native'
class Footer extends Component {
constructor(props) {
super(props);
}
static navigationOptions = ({ navigation }) => ({
title: ''
})
_pageAbout = () => {
this.props.navigation.navigate('About');
}
render() {
return (
<View>
<View style={{ margin: 15 }}>
<TouchableOpacity
onPress={()=>this._pageAbout()}>
<Text>About</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
export default Footer
Then I import and call that component on Home.js screen.
Home.js
import React, { Component } from 'react'
import {
View,
Text,
Linking,
StyleSheet,
Image,
TouchableOpacity
Dimensions,
} from 'react-native'
class Home extends Component {
constructor(props) {
super(props);
}
static navigationOptions = ({ navigation }) => ({
title: ''
})
render() {
return (
<Footer />
);
}
}
export default Home
But when I click "About" text, it returns
TypeError: undefined is not an object (evaluating
this.props.navigation.navigate) in react native
Please help. Thanks before.
You need to pass navigation as a prop to the Footer
<Footer navigation={this.props.navigation} />
As the Footer will be unaware of the navigation stack
send Home props to footer as below
<Footer {...this.props} />

undefined is not a function (evaluating '_this2.closeDrawer()') error in react native app

I am beginner of react native. I am using NativeBase Components for designing. When I use drawer app giving this error
undefined is not a function (evaluating '_this2.closeDrawer()')
This is screenshot of error
Drawer Code is here
import React, { Component } from 'react';
import { Drawer } from 'native-base';
import SideBar from './sidebar';
import Signup from './signup';
import { View } from 'react-native';
export default class NativeDrawer extends Component {
render() {
closeDrawer = () => {
this.drawer._root.close()
};
openDrawer = () => {
this.drawer._root.open()
};
return (
<View>
<Drawer
ref={(ref) => { this.drawer = ref; }}
content={<SideBar navigator={this.navigator} />}
onClose={() => this.closeDrawer()} >
</Drawer>
</View>
);
}
}
SideBar code is here
import React, { Component } from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default class SideBar extends Component {
render() {
return (
<View style={styles.container} >
<Text>
Hello World
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Other NativeBase components are working but Drawer is giving an error
You are setting the prop onClose for your <Drawer> component by using a reference to this.closeDrawer.
This will look for the definition of closerDrawer on your component but you have defined it within your render method.
Defining closeDrawer in the scope of the Component should fix your issue (N.B. I've also moved openDrawer out of render as well):
export default class NativeDrawer extends Component {
// Moved outside of render:
closeDrawer = () => {
this.drawer._root.close()
};
openDrawer = () => {
this.drawer._root.open()
};
render() {
return (
<View>
<Drawer
ref={(ref) => { this.drawer = ref; }}
content={<SideBar navigator={this.navigator} />}
onClose={() => this.closeDrawer()} >
</Drawer>
</View>
);
}
}
Most simple: just remove the "this." in front of closeDrawer()
I'm running the same example code and that fixed it for me!

Undefined is not an object (evaluating 'this.props.navigator.replace')

I followed a tutorial to use React-Navigation, I got a problem when trying to move to another page. I get this error:Undefined is not an object (evaluating 'this.props.navigator.replace')
I searched here and on other sites but nothing worked for me.
This is the code I have:
index:
import React, { Component } from 'react';
import { Root } from './config/Router';
class Application extends Component{
render(){
return <Root />;
}
}
export default Application;
Router:
import React from 'react';
import { StackNavigator } from 'react-navigation';
import Login from '../pages/Login';
import Home from '../pages/Home';
export const Root = StackNavigator({
Login:{
screen: Login,
},
Home:{
screen: Home,
navigatorOptions:{
title: "Homepage"
}
}
});
Login (when clicking the button here, the problem occurs):
export default class Login extends Component{
constructor(props){
super(props);
}
_navigate(routeName){
this.props.navigator.replace({
name: routeName
});
}
render(){
return(
<View style = {styles.container}>
<TouchableOpacity onPress={this._navigate.bind(this, 'Home')}>
<Text>
Hello.
</Text>
</TouchableOpacity>
</View>
);
}
}
What am I doing wrong ?
Thanks in advance.
According to the React Native docs, you'll want to navigate using this.props.navigation, not this.props.navigator:
export default class Login extends Component{
constructor(props){
super(props);
}
_navigate(routeName){
this.props.navigation({
name: routeName
});
}
render(){
return(
<View style = {styles.container}>
<TouchableOpacity onPress={this._navigate.bind(this, 'Home')}>
<Text>
Hello.
</Text>
</TouchableOpacity>
</View>
);
}
}
class HomeScreen extends React.Component {
//Some code here
}
class ChatScreen extends React.Component {
//Some code here
}
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
Chat: { screen: ChatScreen },
});
AppRegistry.registerComponent('HelloWorld', () => SimpleApp);
You should register "Simple" rather than "HomeScreen"

Categories

Resources