React Native Navigation Component Not Rendering - javascript

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,

Related

this.props.navigation.navigate() not working

I want the app to check if there's a user logged in (firebase authentication). If there is, navigate to one page (react navigation v5), if there isn't, to another.
I have the following code:
In App.js
//import navigation + screens
const Stack = createStackNavigator();
const MaterialBottomTabs = createMaterialBottomTabNavigator();
class App extends React.Component {
AuthStack = () => {
<Stack.Navigator>
<Stack.Screen name="login" component={LoginScreen} />
<Stack.Screen name="register" component={RegisterScreen} />
</Stack.Navigator>
}
createBottomTabs = () => {
return <MaterialBottomTabs.Navigator>
<MaterialBottomTabs.Screen name="first" component={firstScreen} />
<MaterialBottomTabs.Screen name="second" component={secondScreen} />
</MaterialBottomTabs.Navigator>
}
render(){
return(
<LoadingScreen />
)
}
}
export default App;
In Loading.js
import React from "react";
import { Text, SafeAreaView } from "react-native";
import * as firebase from "firebase";
import {AuthStack, createBottomTabs} from "./App.js";
class LoadingScreen extends React.Component {
componentDidMount() {
firebase.auth().onAuthStateChanged((user) => {
this.props.navigation.navigate(user ? createBottomTabs : AuthStack);
});
}
render() {
return (
<SafeAreaView>
<Text>
Waiting...
</Text>
</SafeAreaView>
);
}
}
export default LoadingScreen;
I get an error which says:
TypeError: undefined is not an object (evaluating '_this.props.navigation.navigate')
The navigation prop is not passed into all components, only screen components receive this prop automatically! If, however, you wish to access the navigation prop in any of your components, you may use the useNavigation hook.
Here is an example from the docs
import * as React from 'react';
import { Button } from 'react-native';
import { useNavigation } from '#react-navigation/native';
function MyBackButton() {
const navigation = useNavigation();
return (
<Button
title="Back"
onPress={() => {
navigation.goBack();
}}
/>
);
}

How can I use theme functionality with JSS?

I've a problem to integrate the theming functionality inside my next.js project who use react-jss. I tried the ThemeProvider who I've found inside the documentation.
Everytime, my front-end page refresh two times. The first times, I can see that the CSS theme is apply but fews milliseconds later, the page refresh and the theme disappear.
Do you have an idea to fix my problem? Here are my files:
_document.jsx
import React from 'react';
import Document, { Head, Main, NextScript } from 'next/document';
import {
SheetsRegistry,
JssProvider,
ThemeProvider,
} from 'react-jss';
const theme = {
colorPrimary: 'green',
};
export default class JssDocument extends Document {
static getInitialProps(ctx) {
const registry = new SheetsRegistry();
const page = ctx.renderPage(App => props => (
<ThemeProvider theme={theme}>
<JssProvider registry={registry}>
<App {...props} />
</JssProvider>
</ThemeProvider>
));
return {
...page,
registry,
};
}
render() {
return (
<html lang="en">
<Head>
<style id="server-side-styles">
{this.props.registry.toString()}
</style>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
index.jsx
import React from 'react';
import injectSheet from 'react-jss';
import PropTypes from 'prop-types';
const styles = theme => ({
myButton: {
backgroundColor: 'black',
color: theme.colorPrimary,
},
});
const Index = ({ classes }) => (
<div className={classes.myButton}>Welcome to Novatopo website!</div>
);
Index.propTypes = {
classes: PropTypes.shape({
myButton: PropTypes.string,
}).isRequired,
};
export default injectSheet(styles)(Index);
_app.jsx
import App from 'next/app';
export default class MyApp extends App {
componentDidMount() {
const style = document.getElementById('server-side-styles');
if (style) {
style.parentNode.removeChild(style);
}
}
}
Here a CodeSandbox to reproduce the problem: codesandbox.io/s/pyrznxkr1j
2 things you could do:
prepare a codesandbox example
use ThemeProvider inside of JssProvider
I resolved the problem. You need to integrate the ThemeProvider logic inside the _app.jsx file.
Like that:
export default class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
);
}
}

react navigation tab navigator inside static layout

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} />
);
}
}

React Native (Netflix app) Android build error - "Could not find 'store' in either the context or props of Connect(App)"

I am trying to make the android build like 'Netflix' app using this GitHub project from https://github.com/mariodev12/react-native-netflix. As it is a react native project, it works fine in iOS. But while taking android build, it results the following error: 'Could not find 'store' in either the context or props of "Connect(App)" Either wrap the root component in a or explicitly pass "store" as a prop to "Connect(App)"
The code of index.android.js are as follows:
import { AppRegistry } from 'react-native';
import App from './src/app'
AppRegistry.registerComponent('NetflixApp', () => App);
and App.js is:
import React, {Component} from 'react'
import {
Text,
View,
StyleSheet
} from 'react-native'
import {connect} from 'react-redux'
import Header from './components/Header'
import List from './components/List'
import Menu from './components/Menu'
import Slide from './components/Slider'
import Genres from './components/Genres'
import SideMenu from 'react-native-side-menu'
class App extends Component {
constructor(props){
super(props)
this.state = {
isOpen: false,
itemSelected: 'Home'
}
this.getTwoRows = this.getTwoRows.bind(this)
this.itemSelected = this.itemSelected.bind(this)
}
static navigationOptions = {
headerVisible: false
}
toggle(){
this.setState({
isOpen: !this.state.isOpen
})
}
itemSelected(item){
this.setState({
itemSelected: item,
isOpen: false
})
}
updateMenu(isOpen){
this.setState({isOpen})
}
getTwoRows(){
const {shows} = this.props
const array = shows.slice(0)
const val = Math.floor(array.length / 2)
const newArray = array.splice(0, val)
return [
array,
newArray
]
}
render(){
return (
<View style={{flex: 1}}>
<SideMenu
menu={<Menu
navigation={this.props.navigation}
itemSelected={this.itemSelected}
itemSelectedValue={this.state.itemSelected}
/>}
isOpen={this.state.isOpen}
onChange={(isOpen) => this.updateMenu(isOpen)}
style={{flex: 1}}
>
<View style={[{flex: 1}, styles.container]}>
<Header navigation={this.props.navigation} toggle={this.toggle.bind(this)} />
{this.state.itemSelected == 'Home' ? <View style={{flex: 1}}>
<Slide />
<List
getTwoRows={this.getTwoRows}
navigation={this.props.navigation}
/>
</View> :
<Genres
navigation={this.props.navigation}
item={this.state.itemSelected}
/>}
</View>
</SideMenu>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'black'
}
})
export default connect(state => ({shows: state.shows}))(App)
I am struggling with this for last one week. Could you please help me to resolve this issue? Thanks.
You are missing the part where you create a redux store and provide it to your component using Provider. Take a look at the Redux docs for more info.
import { AppRegistry } from 'react-native';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './src/app'
const store = createStore(/* your reducers / middleware */);
const AppWithStore = () => (
<Provider store={store}>
<App />
</Provider>
)
AppRegistry.registerComponent('NetflixApp', () => AppWithStore);
Android and iOS index are pointing to different app file. This error message is caused due missing of a Provider given to app.js file. You can try wrapping your return content with a Provider, just as index.js file is set.

Issue with StackNavigator: Route 'X' should declare a screen

The specific error is "Route 'Camera' should declare a screen".
I have index.js which imports Camera.js and renders the camera component. Camera.js imports router.js which imports all of my screens and creates a StackNavigator. I'm new to React so there's probably something I'm not understanding. Here's the code...
Index.js
import React, { Component } from 'react';
import { CameraView } from './screens/Camera'
class App extends Component {
render() {
return <CameraView />;
}
}
export default App;
Camera.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
} from 'react-native';
import { AppStack } from '../config/router';
....
export default class CameraView extends Component {
viewProducts = () => {
this.props.navigation.navigate('Products');
};
render() {
const { navigate } = navigation;
return (
<View style={styles.container}>
<Camera
ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}
onBarCodeRead={this.readBarcode}>
<Button
onPress={() => this.viewProducts}
title="Products"
/>
</Camera>
</View>
);
}
}
Router.js
import React from 'react';
import { StackNavigator } from 'react-navigation';
import { Icon } from 'react-native-elements';
import Products from '../screens/Products';
import ProductDetail from '../screens/ProductDetail';
import CameraView from '../screens/Camera';
export const Root = StackNavigator({
Camera: {
screen: CameraView,
},
ProductDetail: {
screen: ProductDetail,
navigationOptions: {
title: ({ state }) => `${state.params.name}}`
},
},
Products: {
screen: Products,
navigationOptions: {
title: 'Products',
},
},
});
I'm importing CameraView here and using it as the screen for my Camera route. Is CameraView not considered a screen? Thanks.
Figured the problem out. Instead of rendering
render() {
return <CameraView />;
}
in my index.js I needed to import the router.js and render
render() {
return <Root />;
}

Categories

Resources