Navigator, persist footer&header between transition - reactjs

The Navigator has props for a navigationBar. The docs say:
"Optionally provide a navigation bar that persists across scene transitions"
Using that, when there is a transition to a new screen, only the content will get animated while the navigation bar doesn't change, i.e. it persists.
However, using the navigationBar props I can either have a header OR a footer that persists across transitions. I would like to have both: Header and Footer should both persist across scene transitions.
I've added an example here:
RN Play Example
If you click on 'TestComponent' or 'TestComponent2' you will see the next screen transition in from the right while the navbar at the bottom is NOT part of the transition. How can I add a header that will also NOT be part of the transition?
Is that possible with the navigator?

We've not found a good solution to this so we rolled our own and it has actually worked very very well in a fairly complex app. Here is the gist of what our custom navigator component looks like:
var App = React.createClass({
render() {
return (
<View style={{ flex:1 }}>
<Header />
<AppNavigator />
<Footer />
</View>
)
}
})
The great thing about this is that we have a lot of control over the header and footer. I've set up a basic working example of what I'm talking about here, and pasted the code below.
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
Navigator,
TouchableHighlight
} = React;
var Header = () => (
<View style={ styles.nav }>
<Text>PERSISTENT Header</Text>
</View>
)
var Footer = () => (
<View style={ styles.footer }>
<Text>PERSISTENT Footer</Text>
</View>
)
var AppNavigator = React.createClass({
renderScene(route, navigator) {
return React.createElement(route.component, { ...this.props, ...route.passProps, navigator, route } )
},
render() {
return (
<Navigator
style={{ flex:1 }}
initialRoute={{ id: '0', component: Home, title: 'Home' }}
renderScene={ this.renderScene }
/>
)
},
})
var App = React.createClass({
render() {
return (
<View style={{ flex:1 }}>
<Header />
<AppNavigator />
<Footer />
</View>
)
}
})
var Home = React.createClass({
navigate(route, someprops) {
this.props.navigator.push({
component: route,
passProps: {
someprops: someprops
}
})
},
render: function() {
return (
<View style={styles.container}>
<Text>Hello from Home</Text>
<TouchableHighlight onPress={ () => this.navigate(About, 'ABOUT MESSAGE!!!') } style={ styles.button }>
<Text>Go to about</Text>
</TouchableHighlight>
</View>
);
}
});
var About = React.createClass({
render() {
return (
<View>
<Text>HEllo from About</Text>
<Text>These are the props: { this.props.someprops }</Text>
</View>
)
}
})
var styles = StyleSheet.create({
container: {
flex: 1,
},
button: {
height:50,
backgroundColor: '#efefef',
borderWidth:1,
borderColor: '#ededed'
},
nav: {
height: 60,
backgroundColor: '#ededed',
alignItems: 'center',
justifyContent: 'center'
},
footer: {
height: 60,
backgroundColor: '#ededed',
alignItems: 'center',
justifyContent: 'center'
}
});
AppRegistry.registerComponent('App', () => App);

Related

How to Avoid Text String Must be rendered within a <Text> component Issue?

I get the following error:
Uncaught Error:Invariant Violation: Text string must be renderd within
a component. this error is located at: in RCTView in RCTView in
p in v in RCTView in RCTView in c
Code:
import React from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
export default class App extends React.Component {
state = {
placeName: "",
places: []
}
placeNameChangedHandler = val => {
this.setState({
placeName: val
});
};
placeSubmitHandler = () => {
if (this.state.placeName.trim() === "") {
return;
}
this.setState(prevState => {
return {
places: prevState.places.concat(prevState.placeName)
};
});
};
render() {
const placesOutput = this.state.places.map((place,i) => (
<Text key={i}>
</Text>
));
return (
<View style={styles.container}>
<View style={styles.inputContainer}>
<TextInput
placeholder="Enter Your Name?"
defaultValue={this.state.placeName}
onChangeText={this.placeNameChangedHandler}
style={styles.PlaceInput}
></TextInput>
<Button
title="Add"
style={styles.placeButton}
onPress={this.placeSubmitHandler} />
</View>
<View> </View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 26,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: "flex-start"
},
inputContainer: {
// flex:1,
flexDirection: "row",
justifyContent: "space-between",
width: "100%"
},
PlaceInput: {
width: "70%"
},
placeButton: {
width: "30%"
}
});
I believe it is related to the use of the map() function, if you use it for JSX you should explicitly return (implicit return for JSX is somehow not supported):
render() {
const placesOutput = this.state.places.map((place,i) => {
return(<Text key={i}> </Text>);
})
But I don't really see what you are doing with this placesOutput variable?
Actually, Self Closing For Components working fine for me, As the following:
<View/>
Instead of:
<View> </View>
And
<TextInput
placeholder="Enter Your Name?"
defaultValue={this.state.placeName}
onChangeText={this.placeNameChangedHandler}
style={styles.PlaceInput}
/>
Instead of:
<TextInput
placeholder="Enter Your Name?"
defaultValue={this.state.placeName}
onChangeText={this.placeNameChangedHandler}
style={styles.PlaceInput}
></TextInput>

Can't use react-native-snap-carousel

I would like use react-native-snap-carousel but when I try to init in I have an error :(
the exemple :
import Carousel from 'react-native-snap-carousel';
export class MyCarousel extends Component {
_renderItem ({item, index}) {
return (
<View style={styles.slide}>
<Text style={styles.title}>{ item.title }</Text>
</View>
);
}
render () {
return (
<Carousel
ref={(c) => { this._carousel = c; }}
data={this.state.entries}
renderItem={this._renderItem}
sliderWidth={sliderWidth}
itemWidth={itemWidth}
/>
);
}}
My code :
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Carousel from 'react-native-snap-carousel';
export default class App extends React.Component {
_renderItem ({item, index}) {
return (
<View style={styles.slide}>
<Text style={styles.title}>{ item.title }</Text>
</View>
);}
render () {
return (
<Carousel
ref={(c) => { this._carousel = c; }}
data={this.state.entries}
renderItem={this._renderItem}
sliderWidth={150}
itemWidth={100}
/>
);
}}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
}});
Screenshot
the same on app.js react Native
I'have see a issue (the same like me)
link to Github Issue
But not answer and issue be close
As the screenshot says, this.state.entries is null.
You must initialize it :
export default class App extends React.Component {
constructor() {
super()
this.state = {
entries: [],
}
}
_renderItem ({item, index}) {
return (
<View style={styles.slide}>
<Text style={styles.title}>{ item.title }</Text>
</View>
);}
render () {
return (
<Carousel
ref={(c) => { this._carousel = c; }}
data={this.state.entries}
renderItem={this._renderItem}
sliderWidth={150}
itemWidth={100}
/>
);
}}
In this example, entries: [] wont display anything since there's no object in it. You can initialize it with wanted data:
entries: [
{ title: 'hello' },
{ title: 'world' },
]
Btw, this issue has nothing to do with the plugin itself, even if they could catch it.

Expo Camera only opening once with React Navigation

I set the Expo camera to open on the middle tab with react navigation. However, the camera only opens when I click on that tab the first time. If I switch off of it and go back it's just a black screen. Also the take a picture button is not there. (I am new with react native and kinda coding as a whole)
'use strict';
import React, { Component } from 'react';
import { createBottomTabNavigator } from 'react-navigation';
import { Camera, Permissions } from 'expo';
import {
AppRegistry,
Dimensions,
StyleSheet,
Text,
TouchableOpacity,
View,
Button
} from 'react-native';
class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
</View>
);
}
}
class CameraView extends React.Component {
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
};
async componentWillMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' });
}
render() {
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
} else {
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={this.state.type}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
this.setState({
type: this.state.type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back,
});
}}>
<Text
style={{ fontSize: 18, marginBottom: 10, color: 'white' }}>
{' '}Flip{' '}
</Text>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
}
}
class SettingsScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
</View>
);
}
}
export default createBottomTabNavigator({
Home: HomeScreen,
Camera:CameraView,
Settings: SettingsScreen
});
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
top: 250
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
padding: 15,
paddingHorizontal: 20,
alignSelf: 'center',
margin: 20
}
});
With react navigation 5.x
import { useIsFocused } from '#react-navigation/native';
export const CameraView = (props) => {
const isFocused = useIsFocused();
return (
<View>
{ isFocused && <RNCamera /> }
</View>
)
}
useIsFocused Documentation
I had some issue.
This code solved it:
import { useIsFocused } from '#react-navigation/native';
function MyComponent() {
const isFocused = useIsFocused()
return (
<View>
{ isFocused && <RNCamera /> }
</View>
)
}
Old navigation
import { withNavigationFocus } from 'react-navigation'
render() {
const { isFocused } = this.props
return (
<View>
{ isFocused && <RNCamera ... /> }
</View>
)
}
export default withNavigationFocus(Camera)
To make this work you need to:
1.
import { NavigationEvents } from 'react-navigation';
state = { loaded: true }
render() {
const { loaded } = this.state;
return (
<View style={styles.container}>
<NavigationEvents
onWillFocus={payload => this.setState({loaded: true})}
onDidBlur={payload => this.setState({loaded: false})}/>
<View style={styles.cameraArea}>
{loaded && (
<Camera
type={Camera.Constants.Type.back}
ref={ref => {
this.camera = ref;
}}
/>
)}
</View>
The idea is to hide this camera view (onDidBlur-> loaded: false), then when you come back (onWillFocus is triggered and change loaded to true). When render() function is called it will show the <Camera /> again.
If you have any questions, feel free to ask.
This works for me. ( Navigation 5.x )
if you are using a different screen for CAMERA, you can easy unmount the screen when moving to another.
Reason of this behavior : Only one Camera preview can be active at any
given time. If you have multiple screens in your app, you should
unmount Camera components whenever a screen is unfocused.
<Stack.Screen name="camera" component={CameraScreen} options={{unmountOnBlur: true}}/>
</Stack.Navigator>
Documentation Link : https://docs.expo.io/versions/latest/sdk/camera/
Thanks.
I got it working by using NavigationEvents to determine if the tab is in focus and from there mount and unmount the camera. This also frees up the camera, if you need to use it in another screen. Here is what I would do in your example:
import { NavigationEvents } from 'react-navigation';
...
class CameraView extends React.Component {
constructor(props) {
super(props)
this.state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
isFocused:true
};
}
...
render(){
//...your existing if's
} if(this.state.isFocused === false){
return (
<NavigationEvents
onWillFocus={payload => {
//console.log("will focus", payload);
this.setState({isFocused:true})
}}
onDidBlur={payload => {
//console.log('did leave',payload)
this.setState({isFocused:false})
}}
/>
)
}
} else {
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={this.state.type}>
<NavigationEvents
onWillFocus={payload => {
//console.log("will focus", payload);
this.setState({isFocused:true})
}}
onDidBlur={payload => {
//console.log('did leave',payload)
this.setState({isFocused:false})
}}
/>
//...the rest of your camera code
}
I hope it works for you as well
I solved it using the hook useIsFocused from react-navigation/native. I tested it on Android, iOS and Web
import { useIsFocused } from '#react-navigation/native';
...
const isFocused = useIsFocused();
...
return (
isFocused && (
<Camera
ref={(ref) => {
camera = ref;
}}
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
)
);
If you are using RN Expo with React Navigation - Tab Navigator.
Then just use unmountOnBlur - unmountOnBlur Documentation
This will force the camera to unmount on every navigation focus changes.
in Expo react native
import { useIsFocused } from '#react-navigation/native';
const isFocused = useIsFocused();
{isFocused &&
<Camera
type={type}
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={{ flex: 1 }}
flashMode={flash}
zoom={zoom}
/>
}

React Native Navigation with React Native Admob About

I created a 3 page application with React native navigation. Admob ads are on the 3rd page. I want to try the same ad code on all three screens. If there is any idea in this matter, please share. Thank you.
For better understanding I give the following expo code.
import React, { Component } from 'react';
import {
WebView,
AppRegistry,
StyleSheet,
Text,
View,
Button,
Alert
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import ListComponent from './ListComponent';
class App extends Component {
static navigationOptions = {
title: 'App',
};
OpenSecondActivityFunction = () => {
this.props.navigation.navigate('Second');
};
render() {
return (
<View style={styles.container}>
<Button
onPress={this.OpenSecondActivityFunction}
title="Open Second Activity"
/>
</View>
);
}
}
class SecondActivity extends Component {
static navigationOptions = {
title: 'SecondActivity',
};
OpenThirdActivityFunction = data => {
this.props.navigation.navigate('Third');
};
render() {
return (
<View style={{ flex: 1 }}>
<ListComponent
OpenThirdActivityFunction={this.OpenThirdActivityFunction}
/>
</View>
);
}
}
class ThirdActivity extends Component {
static navigationOptions = {
title: 'ThirdSecondActivity',
};
render() {
return (
<View style={{ flex: 1 }}>
<Text>3</Text>
</View>
);
}
}
const ActivityProject = StackNavigator({
First: { screen: App },
Second: { screen: SecondActivity },
Third: { screen: ThirdActivity },
});
export default ActivityProject;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
Listcomponent.js
import React, { Component } from 'react';
import {
AppRegistry,
View,
Text,
FlatList,
ActivityIndicator,
} from 'react-native';
import { List, ListItem, SearchBar } from 'react-native-elements';
class ListComponents extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false,
};
}
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '98%',
backgroundColor: '#CED0CE',
marginLeft: '2%',
}}
/>
);
};
renderHeader = () => {
return <SearchBar placeholder="Type Here..." lightTheme round />;
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: '#CED0CE',
}}>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={[{ name: 1, coders: 2 }]}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name}`}
subtitle={item.coders}
containerStyle={{ borderBottomWidth: 0 }}
onPress={() => this.props.OpenThirdActivityFunction(item.coders)}
/>
)}
keyExtractor={item => item.coders}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
/>
</List>
);
}
}
export default ListComponents;

React Native move navigator text

Hey In react native I'm using and Everything works well so far but the title and the back button text don't line up. (The login is higher then register) Any ideas how I could set this up?
render() {
const titleConfig = {
title: 'login',
tintColor: "white",
}
return(
<View style={styles.bb}>
<NavigationBar
title={titleConfig}
tintColor="black" />
</View>
)
}
You can pass in a react element to title prop with styling to make it align as per your requirements. For e.g:
render() {
const title = <View style={styles.navTitle}>
<Text style={styles.navTitleText}>Login</Text>
</View>;
return (
<View style={styles.bb}>
<NavigationBar
title={title}
tintColor="black"
/>
</View>
);
}
var styles = StyleSheet.create({
navTitleText: {
color: "white",
fontSize: 19,
marginBottom: 4,
}
});
Here's the complete guide about the API https://github.com/react-native-fellowship/react-native-navbar#api

Resources