I have a component that renders a Navigator :
render() {
return (
<View style={{flex: 1}}>
<Navigator
style={{backgroundColor: '#31BC72'}}
initialRoute={Home.route()}
renderScene={(route, navigator) => {
return React.createElement(route.component, _.extend({navigator: navigator}, route.passProps));
}}
configureScene={() => {
return {
...Navigator.SceneConfigs.FadeAndroid,
defaultTransitionVelocity: 10000,
gestures: {}
};
}} />
</View>
);
}
That's what it looks like :
When I push a new route, let's say from the Home component, it looks like this :
I'd like to have on this component only a navigation bar with only the back button. I can't figure how to do that..
Related
I have a case where a feature contains a FlatList full of information, a search bar, sort button, and filter button.
For the sort and filter buttons I need to pull up a modal from the bottom that takes up half the screen.
I understand that React Navigation wants us to only create one 'root' navigator and all other navigators be dependents; however, in this particular case I would very much like to explicitly add a navigator to this page where a user presses on the filter button, brings up the modal, presses a filter option and then have the modal navigate to another filter subpage within the confines of its view, while maintaining the main page content and root navigation state in the background.
I remember implementing this in React Navigation V1.x, but does anyone know how to get around this in V2.x?
Rather than doing it with nested stack navigator and things, I've implemented your requirement using built-in react native modal.
App
import React, { Component } from 'react';
import { createStackNavigator } from 'react-navigation';
import { MainScreen } from './src/screens/MainScreen';
const RootStack = createStackNavigator(
{
MainScreen
},
{
navigationOptions: {
header: null
}
}
);
export default class App extends Component {
render() {
return (
<RootStack />
);
}
}
MainScreen
import { default as React, PureComponent } from 'react';
import { View, Text, Button, Alert, Modal } from 'react-native';
interface Props {
}
interface States {
num: number;
isFilterOneVisible: boolean;
isFilterTwoVisible: boolean;
}
export class MainScreen extends PureComponent<Props, States> {
state = {
num: 0,
isFilterOneVisible: false,
isFilterTwoVisible: false
}
render() {
return (
<View
flex={1}
justifyContent={'space-evenly'}
alignItems={'center'}
>
<Text style={{ fontSize: 50 }}>{this.state.num}</Text>
<Button
title={'CHANGE STATE'}
onPress={() => {
this.setState((prevState: States) => ({
num: prevState.num + 1
}));
}}
/>
{/* Search */}
<Button
title={'Search'}
onPress={() => {
Alert.alert('Search', 'Search clicked');
}}
/>
{/* Sort*/}
<Button
title={'Sort'}
onPress={() => {
Alert.alert('Sort Clicked', 'Sort clicked')
}}
/>
<Button
title={'Filter'}
onPress={() => {
this.setState({
isFilterOneVisible: true
})
}}
/>
{/* Filter Modal 1*/}
<Modal
visible={this.state.isFilterOneVisible}
transparent={true}
animationType={'slide'}
onRequestClose={() => {
this.setState({
isFilterOneVisible: false
})
}}
>
<View
flex={1}
justifyContent={'flex-end'}
backgroundColor={'rgba(0,0,0,0.2)'}
>
{/* Bottom */}
<View
justifyContent={'center'}
alignItems={'center'}
backgroundColor={'white'}
height={200}
>
<Button
title={'GO TO NEXT FILTER STATE'}
onPress={() => {
this.setState({
isFilterOneVisible: false,
isFilterTwoVisible: true
})
}}
/>
</View>
</View>
</Modal>
{/* Filter Modal Two */}
<Modal
visible={this.state.isFilterTwoVisible}
transparent={true}
animationType={'slide'}
onRequestClose={() => {
this.setState({
isFilterTwoVisible: false
})
}}
>
<View
flex={1}
justifyContent={'flex-end'}
backgroundColor={'rgba(0,0,0,0.2)'}
>
{/* Bottom */}
<View
justifyContent={'center'}
alignItems={'center'}
backgroundColor={'white'}
height={200}
>
<Button
title={'SET DATA AS 1000'}
onPress={() => {
this.setState({
isFilterTwoVisible: false,
num: 1000
})
}}
/>
</View>
</View>
</Modal>
</ View >
);
}
}
NOTE: The code is not optimised and follows some bad patterns like arrow-methods-in-jsx. This is just a suggestion with a working example. Feel free to enhance the code and follow the divide-and-conquer strategy ;) . The full source code can be found from here
I've been using wix/react-native-navigation package to navigate between screens and handling the stack properly.
Moving across screens is pretty straightforward, firing those transitions when a button gets pressed. But the issue comes up when I have a FlatList and I want to push to a new screen when the user taps an item from the list, looks like the navigator props injected at the beginning is lost or in another context than the onPress callback event;
Here is the sample code
class AlertType extends React.PureComponent {
_onPress = () => {
this.props.onPressItem(this.props.itemId, this.props.itemName, this.props.itemImageUrl);
};
render() {
return (
<TouchableOpacity { ...this.props }
onPress={ this._onPress }
style={ itemStyle.cardContainer }>
<View style={ itemStyle.mainContainer }>
<View style={{ width: 10 }}/>
<Image
source={{ uri: NET.HOST + this.props.itemImageUrl }}
style={{ width: 45, height: 45 }}
/>
<View style={{ width: 10 }}/>
<Text style={ itemStyle.itemText }>{ this.props.itemName }</Text>
</View>
</TouchableOpacity>
);
}
}
class AlertsScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
alertTypes: null,
}
}
_onAlertTypePressed(typeId: string, typeName: string, imageUrl: string){
this.props.navigator.push({
screen: 'prod.screens.AlertsCreator',
title: 'Alert',
passProps: {
alertId: typeId,
alertName: typeName,
alertImage: imageUrl
}
});
}
_renderListItem = ({ item }) => (
<AlertType
itemName={ item.titulo }
itemId={ item.key }
itemImageUrl={ item.url }
onPressItem={ this._onAlertTypePressed }
/>
);
render() {
return (
<View style={ styles.mainContainer }>
<FlatList
data={ this.state.alertTypes }
ItemSeparatorComponent={ () => <View style={{ height: 5 }}/> }
renderItem={ this._renderListItem }
/>
</View>
);
}
const mapSessionStateToProps = (state, ownProps) => {
return {
session: state.session
};
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
};
}
export default connect(mapSessionStateToProps, mapDispatchToProps)(AlertsScreen)
This approach produces the next error
There have to be something I'm missing, I know this.props.navigator is not undefined, but inside on _onAlertTypePressed the navigator prop is undefined.
The problem is that you pass function to component without binding it to the current context.
You should pass:
this._onAlertTypePressed.bind(this);
another approach is binding your functions in the constructor:
constructor(props) {
this._onAlertTypePressed = this._onAlertTypePressed.bind(this);
}
I've had this happen before also.
I had to declare navigator between the render and return blocks
render() {
const navigator = this.props.navigator
return()}}
then pass navigator through when calling _onAlertTypePressed
() => _onAlertTypePressed(navigator)
then use navigator vs this.props.navigator inside _onAlertTypePressed
I am developing an APP using React Native now, and I want to make a navigation from every item of a FlatList.
for instance, using create-react-native-app. In App.js, the code below:
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Text>Changes you make will automatically reload.</Text>
<Text>Shake your phone to open the developer menu.</Text>
<FlatList
data={[{key: 'a'}, {key: 'b'}]}
renderItem={({item}) => <Text>{item.key}</Text>}
/>
</View>
);
}
}
when I click an item of FlatList, I want to navigate to another component, I code like this:
export default class App extends React.Component {
cb() {
this.props.navigator.push({
component: QuestionDetail
});
}
render() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Text>Changes you make will automatically reload.</Text>
<Text>Shake your phone to open the developer menu.</Text>
<FlatList
data={[{key: 'a'}, {key: 'b'}]}
renderItem={({item}) => <Text onPress={this.cb.bind(this)}>{item.key}</Text>}
/>
</View>
);
}
}
but there is an error.
can anyone how to make an navigator from an item of FlatList? in fact, every item of FlatList should make an navigator when clicked.
thanks!
Using react-navigation (if you want to use react-native-navigation the flow is pretty similar):
Import StackNavigator:
imports...
import { Text, View, Button, FlatList, TouchableOpacity } from 'react-native';
import { StackNavigator } from 'react-navigation';
Create your stack of screens/containers:
class QuestionDetailScreen extends React.Component {
render() {
return (
<View>
<Text>QuestionDetail Screen</Text>
<Button
title="Go to List"
onPress={() => this.props.navigation.navigate('List')}
/>
</View>
);
}
}
In your List:
class ListScreen extends React.Component {
cb = () => {
this.props.navigation.push('QuestionDetail');
}
render() {
return (
<View>
<Text>Open up App.js to start working on your app!</Text>
<Text>Changes you make will automatically reload.</Text>
<Text>Shake your phone to open the developer menu.</Text>
<FlatList
data={[{key: 'a'}, {key: 'b'}]}
renderItem={({item}) =>
<TouchableOpacity onPress={() => this.cb()}>
<Text>{item.key}</Text>
</TouchableOpacity>}
/>
</View>
);
}
}
And finally export your stacknavigator:
const RootStack = StackNavigator(
{
List: {
screen: ListScreen,
},
QuestionDetail: {
screen: QuestionDetailScreen,
},
},
{
initialRouteName: 'List',
}
);
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
export default class App extends React.Component {
cb() {
this.props.navigator.push({
component: QuestionDetail
});
}
render() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Text>Changes you make will automatically reload.</Text>
<Text>Shake your phone to open the developer menu.</Text>
<FlatList
data={[{key: 'a'}, {key: 'b'}]}
renderItem={({item}) =>
<TouchableOpacity onPress={() => this.cb()}>
<Text>{item.key}</Text>
</TouchableOpacity>}
/>
</View>
);
}
}
You can call the navigation function i.e cb() using a arrow function.
I have used a TouchableOpacity to give the text element onPress functionality.
I have implemented a View component containing a FlatList, which renders TouchableHighlights. Also I have implemented a Modal component, which I'd like to import at various places including the component that renders the FlatList.
I have already managed to open the modal from outside (via handing over a prop for visibility, accessing it via nextProps and setting modals state value "modalVisible" to this) and closing it from inside (simply via changing it's state value "modalVisible").
BUT: I also want to pass data to the modal from each FlatLists item. An item rendered as a TouchableHighlight should open the modal onPress and the modal should contain data from the item (in the code below this would be the items id). How can I achieve passing data to the modal? I somehow can't get it to work using nextProps. This seems more to be an issue related to setting state from within a FlatLists item rather than the Modal.
Modal:
export default class ModalView extends React.Component {
constructor() {
super();
this.state = {
modalVisible: false,
id: null,
};
}
componentWillReceiveProps(nextProps) {
this.setState({
modalVisible: nextProps.modalVisible,
id: nextProps.id,
})
}
render() {
return (
<Modal
animationType="slide"
transparent={ true }
visible={ this.state.modalVisible }
onRequestClose={() => { this.props.setModalVisible(false) }}
>
<View>
<View>
<Text>{ this.state.id }</Text>
<TouchableHighlight
onPress={() => { this.props.setModalVisible(false) }}
>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
)
}
}
FlatList rendering TouchableHighlights:
export default class RecentList extends React.Component {
constructor() {
super();
this.state = {
modalVisible: false,
id: null,
}
}
_onPressItem(id) {
this.setState({
modalVisible: true,
id: id,
});
};
_renderItem = ({item}) => {
return (
<TouchableHighlight
id={item.id}
onPress={this._onPressItem}
>
<View>
<Text>{id}</Text>
</View>
</TouchableHighlight>
)
};
render() {
let data = realm.objects('Example').filtered('completed = true')
.sorted('startedAt', true).slice(0, 10)
return (
<View>
<ModalView
modalVisible={ this.state.modalVisible }
setModalVisible={ (vis) => { this.setModalVisible(vis) }}
id={ this.state.id }
/>
<FlatList
data={data}
renderItem={this._renderItem}
keyExtractor={(item, index) => index}
/>
</View>
)
}
}
A small mistake you have missed ...
_renderItem = ({item}) => {
return (
<TouchableHighlight
id={item.id}
onPress={() => this._onPressItem(item.id)} // Your not sending the item.id
>
<View>
<Text>{id}</Text>
</View>
</TouchableHighlight>
)
};
I'm trying to build a tabview and I can't find out how to change and render scenes. My main view is this one (App.js) :
<View style={{flex: 1}}>
<TabView
ref="tabs"
onTab={(tab) => {
this.setState({tab});
}}
tabs={[
{
component: List,
name: 'Découvrir',
icon: require('../assets/img/tabs/icons/home.png')
},
{
component: Friends,
name: 'Amis',
icon: require('../assets/img/tabs/icons/friend.png'),
pastille: this.state.friendsPastille < 10 ? this.state.friendsPastille : '9+'
},
{
component: RecoStep1,
icon: require('../assets/img/tabs/icons/add.png'),
hasShared: MeStore.getState().me.HAS_SHARED
},
{
component: Notifs,
name: 'Notifs',
icon: require('../assets/img/tabs/icons/notif.png'),
pastille: this.state.notifsPastille < 10 ? this.state.notifsPastille : '9+'
},
{
component: Profil,
name: 'Profil',
icon: require('../assets/img/tabs/icons/account.png')
}
]}
initialSkipCache={!!this.notifLaunchTab}
initialSelected={this.notifLaunchTab || 0}
tabsBlocked={false} />
</View>
The TabView component is this one and it works fine. Only the navigator renders a blank screen only...
renderTab(index, name, icon, pastille, hasShared) {
var opacityStyle = {opacity: index === this.state.selected ? 1 : 0.3};
return (
<TouchableWithoutFeedback key={index} style={styles.tabbarTab} onPress={() => {
if (this.props.tabsBlocked) {
return;
}
this.resetToTab(index);
}}>
<View style={styles.tabbarTab}>
<Image source={icon} style={opacityStyle} />
{name ?
<Text style={[styles.tabbarTabText, opacityStyle]}>{name}</Text>
: null}
</View>
</TouchableWithoutFeedback>
);
}
resetToTab(index, opts) {
this.setState({selected: index});
}
renderScene = (route, navigator) => {
var temp = navigator.getCurrentRoutes();
return temp[this.state.selected].component;
}
render() {
return (
<View style={styles.tabbarContainer}>
<Navigator
style={{backgroundColor: '#FFFFFF', paddingTop: 20}}
initialRouteStack={this.props.tabs}
initialRoute={this.props.tabs[this.props.initialSelected || 0]}
ref="tabs"
key="navigator"
renderScene={this.renderScene}
configureScene={() => {
return {
...Navigator.SceneConfigs.FadeAndroid,
defaultTransitionVelocity: 10000,
gestures: {}
};
}} />
{this.state.showTabBar ? [
<View key="tabBar" style={styles.tabbarTabs}>
{_.map(this.props.tabs, (tab, index) => {
return this.renderTab(index, tab.name, tab.icon, tab.pastille, tab.hasShared);
})}
</View>
] : []}
</View>
);
}
I know I'm doing something wrong, but I can't figure out what ... Changing tabs doesn't display anything as shown below..
I used NavigatorIOS for ans iOS version that worked fine with the following navigator in the render method in TabView (I don't know how to go from the NavigatorIOS to Navigator) :
<Navigator
style={{backgroundColor: '#FFFFFF', paddingTop: 20}}
initialRouteStack={this.props.tabs}
initialRoute={this.props.tabs[this.props.initialSelected || 0]}
ref="tabs"
key="navigator"
renderScene={(tab, navigator) => {
var index = navigator.getCurrentRoutes().indexOf(tab);
return (
<NavigatorIOS
style={styles.tabbarContent}
key={index}
itemWrapperStyle={styles.tabbarContentWrapper}
initialRoute={tab.component.route()}
initialSkipCache={this.props.initialSkipCache} />
);
}}
configureScene={() => {
return {
...Navigator.SceneConfigs.FadeAndroid,
defaultTransitionVelocity: 10000,
gestures: {}
};
}} />
Try adding a
flex:1
property to the navigator. If that doesn't work, check to see that the tabbarContainer also has a
flex:1
property.
OK, I found the answer : I changed my renderScene method to the following :
renderScene = (route, navigator) => {
var temp = navigator.getCurrentRoutes();
return React.createElement(temp[this.state.selected].component, _.extend({navigator: navigator}, route.passProps));
}
Works fine now.