React Native Navigator: Can I remove navigation gestures from a scene after a user action? - react-native

I want to disable the swipe from left pop gesture on the navigator after the side menu has been accessed within a scene. I don't want to disable it when the scene first renders, only when the side menu is open. I have an onOpen function I can call, but I don't know how to programatically change the navigation gestures without pushing another route.
I tried setting the configureScene prop of the navigator like this:
configureScene={() => {
return this.state.swipeBackNavigation ? FloatFromRight : Navigator.SceneConfigs.FloatFromRight;
}
and changing the state, but the component doesn't rerender
ideas would be appreciated

I believe you can just set gestures to null (effectively disabling it):
gestures: {}
I can't test this currently, but I suspect it will work (if I didn't screw up some syntax somewhere):
export default class Foo extends Component {
constructor(props){
super(props);
this.state = {
//initialize gestureChoice
gestureChoice: {},
}
}
disablePop(){
setState({ gestureChoice:{ gestures:{} } });
}
enablePop(){
setState({gestureChoice: ...Navigator.SceneConfigs.FloatFromRight});
}
render(){
return(
<Navigator
renderScene={(route, navigator) =>
return <SomeScene navigator={navigator} {...route.passprops} />
}
configureScene={(route, routeStack) =>
this.state.gestureChoice;
)}
/>
);
}
}
The idea being, you could use enablePop() and disablePop() whenever you would like.
This thread is probably helpful: https://github.com/facebook/react-native/issues/1014

Related

react native webview navigation issue

I am making a test with react navigaton and webview , I have 2 screens , Home and Details , at details screen I am calling / opening a webpage inside webview , let's say that I am calling stackoverflow.com (Page A) , my problem is that when user click a link of the stackoverflow page and navigate and after wants to go back to the previous page (Page A) , it doesn't go , its going or navigating to the Home screen !!!
how can I let The user go back to the previous page. ?
that 's how I am calling the page
<WebView
javaScriptEnabled
source={{uri: 'https://stackoverflow.com/'}}
style={{marginTop: 20}}
/>
As we know built in back button is not provided in iOs but it is provided in android .
So for considering both platform there is two possibility.
Android.
-> For android you have to define BackHandler so here are the step.
import it like this.
import {BackHandler } from 'react-native'.
initialize backhandler inside the life cycle methods.
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
}
handleBackPress = () => {
if (this.state.canGoBack) {
this.refWeb.goBack();
}
else{
this.props.navigation.goBack(null)
}
return true;
}
define a userdefine variable canGoBack inside the status.
constructor(props) {
super(props)
this.state = {
canGoBack: false
}
}
create a method which detect the change in navigation of the webview and bind it with the web view.
onNavigationStateChange(navState) {
this.setState({
canGoBack: navState.canGoBack
});
}
Bind it like this.
<WebView
ref={(myWeb) => this.refWeb = myWeb}
onNavigationStateChange={this.onNavigationStateChange.bind(this)}
source={{ uri: 'https://stackoverflow.com/questions/51712310/react-
native-webview-navigation-issue' }} />
And thsts it you are ready to go..
iOs
For iOs you didn't have to bother too much.
Create a button for back press above the webview or according to your design logic
Follow the above webview and navigation logic . forgot about the backhandler and set this code inside the onPress() method of your created button of backpress
if (this.state.canGoBack) {
this.refWeb.goBack();
}else{
this.props.navigation.goBack(null)
}
Note : Here I use stackNavigator for screen navigation so i used this.props.navigation.goBack(null) this code. if you didn't use it then dont consider this code and replace with your feasible navigator code in else condition
Thankyou..

react-native scrollView - scrollToEnd - on Android

I'm trying to call a function that will fire upon onFoucs on TextInput that will scroll the scrollView all the way down (using scrollToEnd())
so this is my class component
class MyCMP extends Component {
constructor(props) {
super(props);
this.onInputFocus = this.onInputFocus.bind(this);
}
onInputFocus() {
setTimeout(() => {
this.refs.scroll.scrollToEnd();
console.log('done scrolling');
}, 1);
}
render() {
return (
<View>
<ScrollView ref="scroll">
{ /* items */ }
</ScrollView>
<TextInput onFocus={this.onInputFocus} />
</View>
);
}
}
export default MyCMP;
the component above works and it does scroll but it takes a lot of time ... I'm using setTimeout because without it its just going down the screen without calculating the keybaord's height so it not scrolling down enough, even when I keep typing (and triggering that focus on the input) it still doesn't scroll all the way down.
I'm dealing with it some good hours now, I did set the windowSoftInputMode to adjustResize and I did went through some modules like react-native-keyboard-aware-scroll-view or react-native-auto-scroll but none of them really does the work as I need it.
any direction how to make it done the right way would be really appreciated. thanks!
Rather than using a setTimeout you use Keyboard API of react-native. You add an event listener for keyboard show and then scroll the view to end. You might need to create some logic on which input is focused if you have more than one input in your component but if you only have one you can just do it like the example below.
Another good thing to do is changing your refs to functional ones since string refs are considered as legacy and will be removed in future releases of react. More info here.
class MyCMP extends Component {
constructor(props) {
super(props);
this.scroll = null;
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow.bind(this));
}
componentWillUnmount () {
this.keyboardDidShowListener.remove();
}
_keyboardDidShow() {
this.scroll.scrollToEnd();
}
render() {
return (
<View>
<ScrollView ref={(scroll) => {this.scroll = scroll;}}>
{ /* items */ }
</ScrollView>
<TextInput />
</View>
);
}
}
export default MyCMP;
If you have a large dataset React Native docs is telling you to go with FlatList.
To get it to scroll to bottom this is what worked for me
<FlatList
ref={ref => (this.scrollView = ref)}
onContentSizeChange={() => {
this.scrollView.scrollToEnd({ animated: true, index: -1 }, 200);
}}
/>

can't navigate to different screen using react-native-drawer with react-native-router-flux

I'm using react-native-router-flux for the Navigation system and using react-native-drawer for the sidebar. If an user clicks on menu item which is in the sidebar, I want to redirect user to different screen and close the Drawer. I'm using the following code snippets.
Actions.refresh({key: 'drawer', open: false}) to Close the Drawer
Actions.pageTwo.call() to Open the Second Page
Both are working without any problem if I'm using it in separate functions. But, If I trigger both the snippets from the same function. It is not working.
Thanks in Advance,
I have this scenario working for me with both libraries. At a high level I create a custom component to render the content of react-native-drawer which I pass a function to responsible for closing the drawer. Then when I press one of the drawer items I fire both a react-native-router-flux navigation action (in my case a PUSH) and I call the function passed in to close the drawer.
Here is what defining my drawer looks like. Remember DrawerNavigationConten is ultimately just a ListView or whatever implementation you prefer to render the drawer content.
class RootComponent extends Component {
...
closeDrawer() {
this.drawerRef.closeDrawer();
}
render() {
const drawerNavigationContent = (
<DrawerNavigationContent
closeDrawer={this.closeDrawer.bind(this)}
/>
);
return <Drawer
ref={(ref) => { this.drawer = ref; }}
content={drawerNavigationContent}
...
>
...here I define my react-native-router-flux scenes...
</Drawer>
);
}
...
}
Here are the important items in DrawerNavigationContent
import { Actions } from 'react-native-router-flux';
class DrawerNavigationContent extends Component {
...
navigate(location) {
Actions[location]();
this.props.closeDrawer();
}
...
render() {
...
<TouchableOpacity onPress={this.navigate(...some dynamic scene key...)}>
...
</TouchableOpacity>
...
}
}
I've worked with react-native-router-flux and had some issues with the drawer as well. If both scenes are children of the Drawer like so
<Scene key="navigationDrawer" component={NavigationDrawer}>
<Scene key="pageOne" component={PageOne}/>
<Scene key="pageTwo" component={PageTwo}/>
</Scene>
you can use Actions.pageTwo({key: 'navigationDrawer', open: false}) which should close the drawer upon navigation.
Otherwise you could use Actions.refresh({key: 'navigationDrawer', open: false) on PageTwo's componentWillMount or componentDidMount methods.

react-native-router-flux: Navbar buttons need to access scene component methods

I am inserting a rightButton into react-native-router-flux using the renderRightButton api. The problem I ran into is that the rightButton needs to access a method within the component.
class App extends React.Component {
render() {
return(
<Router>
<Scene
key="firstScene"
component={FirstScene}
title="First scene"
rightTitle="Apply"
renderRightButton={this.getRightButton}
/>
</Router>
)
}
getRightButton() {
return(
// someMethodOnFirstSceneComponent lives on the FirstScene component
<View onPress{this.someMethodOnFirstSceneComponent}>
<Text>
Invoke Function from Scene Component
</Text>
</View>
)
}
};
One option would be to put all of the data currently in the state of FirstScene into a redux reducer and then put the someMethodOnFirstSceneComponent on the FirstScene component. However, I am trying to keep state within components and not rely on reducers because from my experience, it keeps things easier to reason about.
I've found a way to do that. Not a very nice solution, but it works. I'm setting onRight method in the component constructor
constructor(props) {
super(props);
this.props.component.onRight = () => {
console.warn("call from my onRight function")}
}
}

Change view in Navigator in React Native

I'm new to react native.
I was using NavigatorIOS but it was too limiting so I'm trying to use Navigator. In NavigatorIOS I can change a view by calling this.props.navigator.push() but it doesn't work in Navigator, it seems to be structured differently. How do I change views in using Navigator?
That's the minimal working navigator - it can do much more (see at the end):
You need your main "navigator" view to render Navigator component
In the Navigator you need to specify how you should render scenes for different routes (renderScene property)
In this "renderScene" method you should render View (scene) based on which route is being rendered. Route is a plain javascript object, and by convention scenes can be identified by "id" parameter of the route. For clarity and separation of concerns I usually define each scene as separate named component and use the name of that components as "id", though it's just a convention. It could be whatever (like scene number for example). Make sure you pass navigator as property to all those views in renderScene so that you can navigate further (see below example)
When you want to switch to another view, you in fact push or replace route to that view and navigator takes care about rendering that route as scene and properly animating the scene (though animation set is quite limited) - you can control general animation scheme but also have each scene animating differently (see the official docs for some examples). Navigator keeps stack (or rather array) of routes so you can move freely between those that are already on the stack (by pushing new, popping, replacing etc.)
"Navigator" View:
render: function() {
<Navigator style={styles.navigator}
renderScene={(route, nav) =>
{return this.renderScene(route, nav)}}
/>
},
renderScene: function(route,nav) {
switch (route.id) {
case "SomeComponent":
return <SomeComponent navigator={nav} />
case "SomeOtherComponent:
return <SomeOtherComponent navigator={nav} />
}
}
SomeComponent:
onSomethingClicked: function() {
// this will push the new component on top of me (you can go back)
this.props.navigator.push({id: "SomeOtherComponent"});
}
onSomethingOtherClicked: function() {
// this will replace myself with the other component (no going back)
this.props.navigator.replace({id: "SomeOtherComponent"});
}
More details here https://facebook.github.io/react-native/docs/navigator.html and you can find a lot of examples in Samples project which is part of react-native: https://github.com/facebook/react-native/tree/master/Examples/UIExplorer
I find that Facebook examples are either to simplistic or to complex when demonstrating how the Navigator works. Based on #jarek-potiuk example, I created a simple app that will switch screens back and forth.
In this example I'm using: react-native: 0.36.1
index.android.js
import React, { Component } from 'react';
import { AppRegistry, Navigator } from 'react-native';
import Apple from './app/Apple';
import Orange from './app/Orange'
class wyse extends Component {
render() {
return (
<Navigator
initialRoute={{screen: 'Apple'}}
renderScene={(route, nav) => {return this.renderScene(route, nav)}}
/>
)
}
renderScene(route,nav) {
switch (route.screen) {
case "Apple":
return <Apple navigator={nav} />
case "Orange":
return <Orange navigator={nav} />
}
}
}
AppRegistry.registerComponent('wyse', () => wyse);
app/Apple.js
import React, { Component } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class Apple extends Component {
render() {
return (
<View>
<Text>Apple</Text>
<TouchableHighlight onPress={this.goOrange.bind(this)}>
<Text>Go to Orange</Text>
</TouchableHighlight>
</View>
)
}
goOrange() {
console.log("go to orange");
this.props.navigator.push({ screen: 'Orange' });
}
}
app/Orange.js
import React, { Component, PropTypes } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class Orange extends Component {
render() {
return (
<View>
<Text>Orange</Text>
<TouchableHighlight onPress={this.goApple.bind(this)}>
<Text>Go to Apple</Text>
</TouchableHighlight>
</View>
)
}
goApple() {
console.log("go to apple");
this.props.navigator.push({ screen: 'Apple' });
}
}
I was having the same trouble, couldn't find a good example of navigation. I wanted the ability to control views to go to a new screen but also have the ability to go back to the previous screen.
I used the above answer by Andrew Wei and created a new app then copied his code. This works well but the .push will keep on creating new layers over each other (Apple > Orange > Apple > Orange > Apple > Orange etc.). So I used .pop in the Orange.js file under goApple() instead of .push.
This works like a "back" button now, which was what I was looking for, while teaching how to navigate to other pages.