I am new in react-native an I am working on a react-native project, I use react-native-navigation from wix and didn't find any solution for how to clear the SplashScreen or any Screen from stack which I don't need to go back again.
I use this to navigate after 2 second.
componentWillMount(){
setTimeout(
() => {
this.props.navigator.push({
screen: 'SampleApp.LoginScreen',
})
}, 2000
);
}
and this in my index.js
export function registerScreens() {
Navigation.registerComponent('SampleApp.SplashScreen', () => SplashScreen);
Navigation.registerComponent('SampleApp.LoginScreen', () => LoginScreen);
}
Please help me to find the solution where I need to call finish() or is there something else. Thanks in advance
You can try this,
import {BackHandler} from 'react-native';
constructor(props) {
super(props)
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
}
onNavigatorEvent(event) {
switch (event.id) {
case 'willAppear':
this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
this.backHandler.remove();
break;
case 'willDisappear':
this.backPressed = 0;
break;
default:
break;
}
}
handleBackPress = () => {
if (this.backPressed && this.backPressed > 0) {
this.props.navigator.popToRoot({ animated: false });
return false;
}
this.backPressed = 1;
this.props.navigator.showSnackbar({
text: 'Press one more time to exit',
duration: 'long',
});
return true;
}
Related
So, here is the problem. I'm using audio html5 element in my react project.
The typical flow for problem is:
the song is playing.
User presses pause on some time(let's say that time is 1.20).
User locks the phone.
After several minutes user unlocks his phone, presses "Play" button and here what happens:
The mediaPositionState count current time as previous(1.20) PLUS current time of the audio, instead of counting just current.
This extra 1.20 is remaining even when changing songs.
I've tried to control it in useEffect below
useEffect(() => {
const audioEl = audioRef.current;
if (audioEl) {
audioEl.addEventListener('timeupdate', updateTime);
audioEl.addEventListener('loadeddata', updatePositionState);
}
return () => {
if (audioEl) {
audioEl.removeEventListener('timeupdate', updateTime);
audioEl.removeEventListener('loadeddata', updateTime);
updatePositionState();
}
};
}, []);
but it works normally only when user is in focus with audio.
Also I have following code:
function updatePositionState() {
if (navigator.mediaSession?.setPositionState) {
navigator.mediaSession.setPositionState({
duration: audioRef.current?.duration ?? 0.0,
position: audioRef.current?.currentTime ?? 0.0,
});
}
}
const createMediaSession = (state: AudioStateType) => {
if (navigator.mediaSession) {
navigator.mediaSession.metadata = new MediaMetadata({
title: state.currentSongName,
artist: state.currentArtistName,
album: state.currentAlbumName,
artwork: [
{
sizes: '300x300',
src: `http://storage.musicstream.app/cover/${state.currentAlbumCoverId}`,
},
],
});
navigator.mediaSession.setActionHandler('play', function () {
dispatch({ type: 'resume' });
updatePositionState();
});
navigator.mediaSession.setActionHandler('pause', function () {
dispatch({ type: 'pause' });
updatePositionState();
});
navigator.mediaSession.setActionHandler('seekto', function (details) {
dispatch({ type: 'manual_update_time', time: details.seekTime });
updatePositionState();
});
navigator.mediaSession.setActionHandler('previoustrack', () => {
return 0;
});
navigator.mediaSession.setActionHandler('nexttrack', () => {
return 0;
});
}
};
I don't know how to normally describe the problem, let's assume that mediaposition messes up when user swipes out the MediaSession notification.
I will provide more code if you ask.
Also I provide the screenshots(despite I tried to force the problem similar one occured: it shows time as the end of track).
current song time
current song time is okay when paused
current song time is messed when playing
Adding updateTime function by request
It is just for updating state in react.Context
const updateTime = () => {
if (audioRef.current) {
dispatch({ type: 'update_time', time: audioRef.current.currentTime });
}
};
Also, the full reducer looks like this(I don't think it would be helpful):
function audioReducer(state: AudioStateType, action: Action): AudioStateType {
switch (action.type) {
case 'fetch_and_play': {
play(action.songData?.currentSongId).then(() => {
dispatch({
type: 'play',
songData: {
...action.songData,
length: audioRef.current?.duration,
},
});
});
return state;
}
case 'play': {
createMediaSession({ ...state, ...action.songData });
return { ...state, ...action.songData };
}
case 'pause': {
pause();
return { ...state, songIsPaused: true };
}
case 'resume': {
resume();
return { ...state, songIsPaused: false };
}
case 'update_time': {
return { ...state, currentTime: action.time };
}
case 'manual_update_time': {
if (audioRef.current) {
audioRef.current.currentTime = action.time;
return { ...state, currentTime: action.time };
} else {
return state;
}
}
default: {
return state;
}
}
}
I made a codesandbox, where you can see my problem. https://codesandbox.io/s/wizardly-wiles-87qi1?file=/src/App.js
In order to truly understand please use an android phone
Reducer should be Pure function, use action before reducer for dispatch and other staff:
Action.ts
const fetchAndPlay = (currentSongId) => {
play(currentSongId).then(() => {
createMediaSession({ ...state, ...action.songData });
dispatch({
type: 'play',
//just pass data u need to show or you want rerender them
length: audioRef.current?.duration,
});
});
}
Reducer.ts
function audioReducer(state: AudioStateType, action: Action): AudioStateType {
switch (action.type) {
case 'play':
return { ...state, length:action.length, playing: true};
...
}
In your component just call this action :
fetchAndPlay(songId)
I get a problem to handle hardware back button android in React Native, I want to back in specific page/component when I press back button in hardware/device, but I always get error 'undefined is not an object (Evaluating 'this.props.navigation').
this is my script :
import { Platform, StyleSheet, Text, View, BackHandler } from 'react-native';
import { createStackNavigator, createAppContainer, NavigationActions, createBottomTabNavigator } from 'react-navigation';
import OnBoarding from './apps/src/onBoarding/OnBoarding';
import Welcome from './apps/src/welcome/Welcome';
import Login from './apps/src/login/Login';
const MainNavigator = createStackNavigator({
OnBoarding: OnBoarding,
Welcome: Welcome,
Login: Login
},{
initialRouteName: 'OnBoarding',
headerMode: 'none',
navigationOptions: {
headerVisible: false
}
});
const Approot = createAppContainer(MainNavigator);
var screen = '';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
routeName: ''
}
}
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
}
handleBackButton() {
if(screen == 'Login') {
this.props.navigation.navigate('OnBoarding');
}else{
return false;
}
}
getActiveRouteName(navigationState) {
if (!navigationState) {
return null;
}
const route = navigationState.routes[navigationState.index];
// dive into nested navigators
if (route.routes) {
return this.getActiveRouteName(route);
}
return route.routeName;
}
render() {
return <Approot onNavigationStateChange={(prevState, currentState) => {
screen = this.getActiveRouteName(currentState)
}} />;
}
}
in this case, I have 3 component, they are OnBoarding, Welcome, Login, when postion in Login I want to back to OnBoarding when press hardware back button, please help me to solve this problem.
Thanks.
You will have to use the navigation services as you want to navigate from outside of navigation (root of the app). you can follow documentation.
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
LoginScreen.js
this.props.navigator.push({
screen: "auxxa.LandingScreen",
passProps: { login: true },
overrideBackPress: true,
navigatorStyle: {
navBarHidden: true
}
});
LandingScreen.js
constructor(props) {
super(props);
this.handleBackButtonClick = this.handleBackButtonClick.bind(this);
// this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
this.state = {
size: { width, height },
tileData: null,
isLoading: true,
user_id: null,
refetching: false,
access_token: null
};
}
componentWillMount() {
BackHandler.addEventListener(
"hardwareBackPress",
this.handleBackButtonClick
);
}
handleBackButtonClick() {
console.log("check login " + this.props.login);
if (this.backPressed && this.backPressed > 0) {
if (this.props.login) {
console.log("login");
RNExitApp.exitApp();
} else {
console.log("root");
this.props.navigator.popToRoot({ animated: false });
return false;
}
}
this.backPressed = 1;
this.props.navigator.showSnackbar({
text: "Press one more time to exit",
duration: "long"
});
return true;
}
componentDidMount() {
BackHandler.removeEventListener(
"hardwareBackPress",
this.handleBackButtonClick
);
}
I used react-native-navigation from Wix for my app nevigation purpose.Here I have attached login screen and landing screen.after successful login app navigate to landing screen.after that I click back button It will return to login screen.I need to avoid that.How can I do that thing? I tried to exit from the app.But it also not working properly.
Please help me if some one know this.Thanks in advanced.
Use this call in handleBackButtonClick function and why are you removing the listener in componentDidMount ?
this.props.navigator.resetTo({ screen: 'example.ScreenThree'})
.
Uncomment this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this)); on your constructor to listen to navigation events
and add the navigationEvent listener method
onNavigatorEvent(event: NavigatorEvent) {
if (event.type === 'NavBarButtonPress') {
if (event.id === 'skill_information') {
// Add here whatever you would like to do (this.handleBackButtonClick() for example)
}
}
I am trying to go back when the user presses back button on Android.
I have added listener to the screen and it's receiving event when in the remotely debug mode. But it's not working properly when I don't do debug remotely. It's really weird.
I am gonna attach code snippets that I have written.
//Navigator
const BoardNavigator = StackNavigator({
Board: { screen: Board }
});
//Board Component
class Board extends Component {
componentWillMount () {
BackHandler.addEventListener('hardwareBackPress', this._onBackPressed);
}
componentWillUnmount () {
BackHandler.removeEventListener('hardwareBackPress', this._onBackPressed);
}
_onBackPressed () {
console.log('backPress');
goBack(this.props.navigation);
return true;
}
onNext() {
this.props.navigation.navigate("Board", {content: ...});
}
}
Additional Info :
This BoardNavigator is the nested one of the rootNavigator(StackNavigator).
react : '16.0.0-alpha.12'
react-native : "0.47.2"
I've actually used backhandler like below for controlling back button to close the application just with 2 press immediately.
componentDidMount() {
this._backPress = 0;
BackHandler.addEventListener('backPress', () => {
setTimeout(() => {
this._backPress = 0;
}, 3000);
this._backPress += 1;
if (this._backPress <= 1) {
ToastAndroid.showWithGravity(strings.BACK_BUTTON_ALERT, ToastAndroid.SHORT, ToastAndroid.CENTER);
return true;
}
return false;
});
After converting the app to redux, my react-navigation got some problem. Previously, before integrating with redux, when I press back button (Physical button) react-navigation back to the previous screen. After integrating with redux, the back button will close the app. But, it's still working with goBack() function.
I'm following the guide: https://reactnavigation.org/docs/guides/redux
And read some code from here : https://github.com/react-community/react-navigation/tree/master/examples/ReduxExample
And, this is my Navigator configuration
export const AppNavigator = StackNavigator(
{
Home: { screen: HomeScreen },
ChatDetail: { screen: ChatDetail },
PulsaDetail: { screen: PulsaDetailScreen },
Pulsa: { screen: Pulsa }
},
{
headerMode: 'none',
}
)
class AppWithNavigation extends Component {
render(){
return(
<AppNavigator navigation={ addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
})} />
)
}
}
const mapStateToProps = (state) => ({
nav: state.nav
})
export default connect(mapStateToProps)(AppWithNavigation)
EDIT: It's can be done with manual handle & dispatch back action, but it's can't do it automaticlly? just like before using redux?
BackHandler.addEventListener('hardwareBackPress',() => {
this.props.goBack()
return true
})
After post Github issue in react-navigation repository, I got the answer.
Should add manually the back listener on top of screen / component
// App.js
import { BackAndroid } from 'react-native'
// [...]
componentDidMount() {
BackAndroid.addEventListener('backPress', () => {
const { dispatch, nav } = this.props
if (shouldCloseApp(nav)) return false
dispatch({ type: 'Back' })
return true
})
}
componentWillUnmount() {
BackAndroid.removeEventListener('backPress')
}
// [...]
https://github.com/react-community/react-navigation/issues/2117
https://github.com/react-community/react-navigation/issues/117
UPDATE:
https://facebook.github.io/react-native/docs/backhandler