React-Native:按下 android 硬件后退按钮返回

Posted

技术标签:

【中文标题】React-Native:按下 android 硬件后退按钮返回【英文标题】:React-Native: Go back on android hardware back button pressed 【发布时间】:2017-01-19 15:23:21 【问题描述】:

我正在尝试在按下 android 后退按钮时添加返回 webview,但我仍然无法使其工作。

这是我的代码:

<WebView
    ref=WEBVIEW_REF
    source=source
    domStorageEnabled=true
    onNavigationStateChange=this.onNavigationStateChange
/>

componentDidMount() 
    BackAndroid.addEventListener('hardwareBackPress', function() 
        if(this.state.backButtonEnabled) 
            this.refs[WEBVIEW_REF].goBack();
            return true;
        
    );
;

onNavigationStateChange = (navState) => 
    this.setState(
        backButtonEnabled: navState.canGoBack,
    );
;

使用上面的代码,我得到错误 undefined is not an object this.state.backButtonEnabled(在状态中设置)。

我只是想看看 goBack 是否有效,所以我删除了 if 语句,然后我得到错误 undefined is not an object this.refs[WEBVIEW_REF]。

什么是最好的解决方案?

【问题讨论】:

如果在render方法中抛出backandroid事件监听器会怎样? 我遇到了同样的错误。 【参考方案1】:

想添加一个完整的例子,以防它帮助任何人:

import React,  Component  from 'react';
import 
  BackHandler,
  Platform,
  WebView,
 from 'react-native';

class ExampleWebView extends Component 
  webView = 
    canGoBack: false,
    ref: null,
  

  onAndroidBackPress = () => 
    if (this.webView.canGoBack && this.webView.ref) 
      this.webView.ref.goBack();
      return true;
    
    return false;
  

  componentWillMount() 
    if (Platform.OS === 'android') 
      BackHandler.addEventListener('hardwareBackPress', this.onAndroidBackPress);
    
  

  componentWillUnmount() 
    if (Platform.OS === 'android') 
      BackHandler.removeEventListener('hardwareBackPress');
    
  

  render() 
    return (
      <WebView
        source= uri: "https://www.google.com" 
        ref=(webView) =>  this.webView.ref = webView; 
        onNavigationStateChange=(navState) =>  this.webView.canGoBack = navState.canGoBack; 
      />
    );
  

【讨论】:

这是一个全面的解决方案。也许你应该创建一个要点? 出现错误 - TypeError: TypeError: undefined is not an object (evalating '_this2.webView.ref = webView') 我想需要绑定onAndroidBackPress @Crisfole 如果您使用箭头函数,就像我在此处的示例中使用的那样。 我很确定还有一些额外的复杂性,尤其是在事件绑定方面。不过我可能是错的。 javascriptweblog.wordpress.com/2015/11/02/…【参考方案2】:
class MyComponent extends Component 
    state = ;
    componentDidMount()
         BackHandler.addEventListener('hardwareBackPress', this.backHandler);
    
    componentWillUnmount()
         BackHandler.removeEventListener('hardwareBackPress', this.backHandler);
    
    backHandler = () => 
        if(this.state.backButtonEnabled) 
            this.refs[WEBVIEW_REF].goBack();
            return true;
        
    

1) 绑定你的处理程序 2) 不要忘记在卸载时删除监听器。

【讨论】:

我确实在 5 分钟前修复了它,这实际上也是我所做的 :) 仍然感谢您的帮助,我肯定也会帮助其他开发人员! BackAndroid 将被弃用,最好使用 BackHandler 像 @RamRovi 回答【参考方案3】:

这是一个带有 Typescript 和 useRefuseEffect 钩子的解决方案。

我没有使用canGoBack,但它似乎无论如何都可以工作。

import React,  useEffect, useRef  from 'react';
import  BackHandler  from 'react-native';
import WebView from 'react-native-webview';

const WebViewWrapper = (): JSX.Element => 
  const webview = useRef<WebView>(null);
  const onAndroidBackPress = (): boolean => 
    if (webview.current) 
      webview.current.goBack();
      return true; // prevent default behavior (exit app)
    
    return false;
  ;
  useEffect((): (() => void) => 
    BackHandler.addEventListener('hardwareBackPress', onAndroidBackPress);
    return (): void => 
      BackHandler.removeEventListener('hardwareBackPress', onAndroidBackPress);
    ;
  , []); // Never re-run this effect
  return (
    <WebView
      source= uri: 'https://***.com' 
      ref=webview
    />
  )

【讨论】:

这条线帮助了我return true; // prevent default behavior (exit app)【参考方案4】:

这可能对某人有所帮助,因为上述解决方案没有解决我的问题....

import React,  Component  from 'react';
import 
  BackHandler,
  WebView,
 from 'react-native';

export default class App extends Component 

constructor(props) 
    super(props);
    this.WEBVIEW_REF = React.createRef();


componentDidMount() 
    BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);


componentWillUnmount() 
  BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);


handleBackButton = ()=>
   this.WEBVIEW_REF.current.goBack();
   return true;


onNavigationStateChange(navState) 
  this.setState(
    canGoBack: navState.canGoBack
  );


render()
   return (
    <WebView
        source= uri: "https://www.cbt.ng" 
        ref=this.WEBVIEW_REF
        onNavigationStateChange=this.onNavigationStateChange.bind(this)
     />
    )

 

【讨论】:

【参考方案5】:

如果您正在寻找functional component 解决方案。

注意:执行返回操作不需要canGoBack状态,它只是为了保存当前状态,你可以放心删除它

import React,  useState, useEffect, useRef  from "react"
import  BackHandler, Platform  from "react-native"
import  SafeAreaView  from "react-navigation"
import  WebView  from "react-native-webview"

const Webview = () => 
  const webView = useRef(null);
  const [canGoBack, setCanGoBack] = useState(false);

  useEffect(() => 
    if (Platform.OS === 'android') 
      BackHandler.addEventListener('hardwareBackPress', HandleBackPressed);

      return () => 
        BackHandler.removeEventListener('hardwareBackPress', HandleBackPressed);
      
    
  , []); // INITIALIZE ONLY ONCE

  const HandleBackPressed = () => 
    if (webView.current) 
      webView.current.goBack();
      return true; // PREVENT DEFAULT BEHAVIOUR (EXITING THE APP)
    
    return false;
  

  return (
    <SafeAreaView>
      <WebView
        ref=webView
        source=
          uri: "<YOUR_URL>"
        
        onNavigationStateChange=navState => setCanGoBack(navState.canGoBack)
      />
    </SafeAreaView>
  )


export default Webview;

【讨论】:

请不要使用状态来维护canGoBack,因为改变状态会导致重新渲染,而是使用refs 嘿,这是可行的,但是从应用程序中,我们无法在按下后退按钮时退出,就像在家里我们按下后退一样,所以它应该退出但它没有退出应用程序 历史长度不小于2才会退出应用【参考方案6】:

不幸的是,onNavigationStateChange 没有为 SPA(单页应用程序)网站触发:https://github.com/react-native-webview/react-native-webview/issues/1510

改用onLoadProgress

import React,  useRef, useState, useCallback, useEffect  from "react";
import  BackHandler  from "react-native";
import  WebView  from "react-native-webview";

export default function App() 
  const webView = useRef();

  const [canGoBack, setCanGoBack] = useState(false);

  const handleBack = useCallback(() => 
    if (canGoBack && webView.current) 
      webView.current.goBack();
      return true;
    
    return false;
  , [canGoBack]);

  useEffect(() => 
    BackHandler.addEventListener("hardwareBackPress", handleBack);
    return () => 
      BackHandler.removeEventListener("hardwareBackPress", handleBack);
    ;
  , [handleBack]);

  return (
    <WebView
      ref=webView
      source= uri: "https://my-awesome-spa.com/" 
      onLoadProgress=(event) => setCanGoBack(event.nativeEvent.canGoBack)
    />
  );

【讨论】:

感谢您的回答。它对我有用......【参考方案7】:

在 react native 中的 webview 的情况下,默认情况下按下移动端的后退按钮时应用程序退出。如果你想在按下返回按钮的时候回到上一页,那么你需要实现react-native webview的“返回”功能。

您可以查看本文的Step 5 : Handle Mobile Back Button部分。

【讨论】:

【参考方案8】:

添加到@Nisharg Shah 答案。对于onNavigationStateChange 道具,添加以下行

onNavigationStateChange=navState => webView.current.canGoBack = navState.canGoBack

HandleBackPressed方法中检查webView.current.canGoBack然后webView.current.goBack();否则return false

import React,  useState, useEffect, useRef  from "react"
import  BackHandler, Platform  from "react-native"
import  SafeAreaView  from "react-navigation"
import  WebView  from "react-native-webview"

const Webview = () => 
const webView = useRef(null);
const [canGoBack, setCanGoBack] = useState(false);

useEffect(() => 
  if (Platform.OS === 'android') 
    BackHandler.addEventListener('hardwareBackPress', HandleBackPressed);

    return () => 
       BackHandler.removeEventListener('hardwareBackPress', HandleBackPressed);
    
  
, []); // INITIALIZE ONLY ONCE

const HandleBackPressed = () => 
    if (webView.current.canGoBack) 
        webView.current.goBack();
        return true; // PREVENT DEFAULT BEHAVIOUR (EXITING THE APP)
    
    return false;


return (
  <SafeAreaView>
    <WebView
      ref=webView
      source=
        uri: "<YOUR_URL>"
      
      onNavigationStateChange=navState => webView.current.canGoBack = navState.canGoBack ->> This line is important
  />
 </SafeAreaView>
 )


export default Webview;

【讨论】:

【参考方案9】:

所有功能组件的答案都有缺陷(比如当webview无法返回时无法使用返回按钮退出应用程序)。

这与最受好评的基于类的答案的行为相同:

import React,  useEffect, useRef, useState  from 'react';
import  BackHandler, Platform  from 'react-native';
import  WebView  from 'react-native-webview';

const App = () => 
  const webview = useRef<WebView>(null);
  const [canGoBack, setCanGoBack] = useState(false);

  const onAndroidBackPress = () => 
    if (canGoBack && webview.current) 
      webview.current.goBack();
      return true;
    

    return false;
  

  useEffect(() => 
    if (Platform.OS === 'android') 
      BackHandler.addEventListener('hardwareBackPress', onAndroidBackPress);
    
    return () => 
      BackHandler.removeEventListener('hardwareBackPress', onAndroidBackPress);
    
  , [canGoBack]);

  return (
    <WebView
      source=uri: 'https://www.google.com'
      ref=webview
      onNavigationStateChange=(navState) => setCanGoBack(navState.canGoBack)
    />
  );
;

export default App;

【讨论】:

以上是关于React-Native:按下 android 硬件后退按钮返回的主要内容,如果未能解决你的问题,请参考以下文章

android studio 中查找代码中的硬编码

在 React-native 中按下按钮后双触发

将按下的项目插入到 react-native 的数组中

PickerIOS 总是显示在 React-Native

在键盘感知滚动视图中的 textInput 后​​按下 React-Native 按钮

Atom : React Native 的快速文档