Home  >  Article  >  Web Front-end  >  How to solve the return processing of react-native WebView without calling it

How to solve the return processing of react-native WebView without calling it

php中世界最好的语言
php中世界最好的语言Original
2018-03-17 13:32:042178browse

This time I will show you how to solve the return processing of react-native WebView without calling it. How to solve the return processing of react-native WebView without calling it. What are the precautions? What are the following? This is a practical case, let’s take a look at it.

1. Preface

There are some pages in the project that change frequently. We will consider using web pages to solve these pages.

Provide a public Web page in the RN project. If it is web content, jump to this interface for display.

At this time, there will be a problem that the web page will have a first-level page and a second-level page, which will be designed to handle the return key of the navigation bar (and the processing of the return key on Android ).

The solution to this problem can be found on the RN official website. Just use the onNavigationStateChange callback method to record the current navigation state, so as to determine whether to return to the previous page or exit this webpage and return to other interfaces of the App.

However, when the implementation of the web page is React, there will be a problem. You will find that when the page jumps, the onNavigationStateChange callback method has no callback! ! ! How fat! !

At first, I tried to change the web address to Baidu's, which can receive callbacks. Everything ran very well, but it didn't work if I changed it to our link, so I passed the blame to the backend, thinking it was React. Which part of the writing is wrong.

Because the last project was tight and I didn’t have time to take a good look at the source code, I thought of a not very complete solution, which is to use js on the web page to call back the App to inform the current navigation status. This solution The display is unfriendly.

Now I have a little time to read the source code and find out the real reason.

2. Reason

Let’s analyze the cause of this problem and my solution.

1. First, find the location of the source code.

node_modules\react-native\ReactAndroid\src\main\java\com\facebook\react\views\webview

node_modules\react-native\Libraries\Components\WebView

Directory structure is like this:

2. Implemented code segment (JAVA side)

The actual running code of RN is native code, so some event callbacks like WebView components are actually triggered by callbacks in native code. As follows

(ReactWebViewManager.java) rn version 0.47.1

protected static class ReactWebViewClient extends WebViewClient { //WebViewClient就是我们在写Android原生代码时,监听网页加载情况使用的工具。
   protected static final String REACT_CLASS = "RCTWebView"; //定义的原生组件名,在后面JS中会对应到。
  //...
  @Override
  public void onPageStarted(WebView webView, String url, Bitmap favicon) { //有很多回调方法,此处只举一例
   super.onPageStarted(webView, url, favicon);
   mLastLoadFailed = false;
   dispatchEvent(
     webView,
     new TopLoadingStartEvent(   //自己定义的时间,dispatch后,事件会传给js
       webView.getId(),
       createWebViewEvent(webView, url)));
  }
  //...
 }

(ReactWebViewManager.java) rn version 0.43.3, different versions of RN will have code adjustments, so when upgrading RN, you need Careful regression testing.

protected static class ReactWebViewClient extends WebViewClient { //WebViewClient就是我们在写Android原生代码时,监听网页加载情况使用的工具。
   protected static final String REACT_CLASS = "RCTWebView"; //定义的原生组件名,在后面JS中会对应到。
  //...
  @Override
  public void onPageStarted(WebView webView, String url, Bitmap favicon) { //有很多回调方法,此处只举一例
   super.onPageStarted(webView, url, favicon);
   mLastLoadFailed = false;
   dispatchEvent(
     webView,
     new TopLoadingStartEvent(   //自己定义的时间,dispatch后,事件会传给js
       webView.getId(),
       createWebViewEvent(webView, url)));
  }
  @Override
  public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) { //坑在这,这里就是导航有变化的时候会回调在这个版本是有这个处理的,但是不知道在哪个版本删掉了 -.-
   super.doUpdateVisitedHistory(webView, url, isReload);
   dispatchEvent(
     webView,
     new TopLoadingStartEvent(
       webView.getId(),
       createWebViewEvent(webView, url)));
  }
  //...
 }

(TopLoadingStartEvent.java) Callback JS Event

public class TopLoadingStartEvent extends Event<TopLoadingStartEvent> {
 public static final String EVENT_NAME = "topLoadingStart";  //对应方法是onLoadingStart, 因为对RN的结构不熟悉,在此处花了很长时间研究是怎么对应的,最后找到了定义对应的文件
 private WritableMap mEventData;
 public TopLoadingStartEvent(int viewId, WritableMap eventData) {
  super(viewId);
  mEventData = eventData;
 }
 @Override
 public String getEventName() {
  return EVENT_NAME;
 }
 @Override
 public boolean canCoalesce() {
  return false;
 }
 @Override
 public short getCoalescingKey() {
  // All events for a given view can be coalesced.
  return 0;
 }
 @Override
 public void dispatch(RCTEventEmitter rctEventEmitter) {
  rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData);
 }
}

(node_modules\react-native\ReactAndroid\src\main\java\com\facebook\react\uimanager\UIManagerModuleConstants.java)

In this file, the corresponding relationship is defined

/**
 * Constants exposed to JS from {@link UIManagerModule}.
 */
/* package */ class UIManagerModuleConstants {
 /* package */ static Map getDirectEventTypeConstants() {
  return MapBuilder.builder()
    .put("topContentSizeChange", MapBuilder.of("registrationName", "onContentSizeChange"))
    .put("topLayout", MapBuilder.of("registrationName", "onLayout"))
    .put("topLoadingError", MapBuilder.of("registrationName", "onLoadingError"))
    .put("topLoadingFinish", MapBuilder.of("registrationName", "onLoadingFinish"))
    .put("topLoadingStart", MapBuilder.of("registrationName", "onLoadingStart"))
    .put("topSelectionChange", MapBuilder.of("registrationName", "onSelectionChange"))
    .put("topMessage", MapBuilder.of("registrationName", "onMessage"))
    .build();
 }
}

3. Implemented code segment (JS side)

(node_modules\react-native\ Libraries\Components\WebView\WebView.android.js)

You can see in the code below that only onLoadingStart and onLoadingFinish will call updateNavigationState. The problem arises here. Since our web page is implemented in React, There is only one page! So onLoadingStart and onLoadingFinish will only be called once. Clicking the details page again will not jump to a new page, but will refresh the original page. So there is no updateNavigationState callback.

class WebView extends React.Component {
 static propTypes = {  //给外部定义的可设置的属性
  ...ViewPropTypes,
  renderError: PropTypes.func,
  renderLoading: PropTypes.func,
  onLoad: PropTypes.func,
  //...
  }
 render() { //绘制页面内容
  //...
  var webView =
   <RCTWebView
    ref={RCT_WEBVIEW_REF}
    key="webViewKey"
    style={webViewStyles}
    source={resolveAssetSource(source)}
    onLoadingStart={this.onLoadingStart}
    onLoadingFinish={this.onLoadingFinish}
    onLoadingError={this.onLoadingError}/>;
  return (
   <View style={styles.container}>
    {webView}
    {otherView}
   </View>
  );
 }
 onLoadingStart = (event) => {
  var onLoadStart = this.props.onLoadStart;
  onLoadStart && onLoadStart(event);
  this.updateNavigationState(event);
 };
 onLoadingFinish = (event) => {
  var {onLoad, onLoadEnd} = this.props;
  onLoad && onLoad(event);
  onLoadEnd && onLoadEnd(event);
  this.setState({
   viewState: WebViewState.IDLE,
  });
  this.updateNavigationState(event);
 };
 updateNavigationState = (event) => {
  if (this.props.onNavigationStateChange) {
   this.props.onNavigationStateChange(event.nativeEvent);
  }
 };
}
var RCTWebView = requireNativeComponent('RCTWebView', WebView, {  //对应上面JAVA中的 ‘RCTWebView'
 nativeOnly: { messagingEnabled: PropTypes.bool, }, });
 module.exports = WebView;

2. Solution

Now that the cause is found, it is easy to solve

Solution: Customize WebView, add doUpdateVisitedHistory processing, in Notify JS every time the navigation changes.

1. Copy the files in the picture below to the Android code directory in our own project

拷贝完后的Android目录:

ReactWebViewManager.java中需要修改几个地方

public class ReactWebViewManager extends SimpleViewManager<WebView> {
 protected static final String REACT_CLASS = "RCTWebView1"; //此处修改一下名字
 protected static class ReactWebViewClient extends WebViewClient {
    @Override
    public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) {
      super.doUpdateVisitedHistory(webView, url, isReload);
      dispatchEvent(    //在导航变化的时候,dispatchEvent
          webView,
          new TopCanGoBackEvent(
              webView.getId(),
              createCanGoBackWebViewEvent(webView, url)));
    }
 }
}

TopCanGoBackEvent是我自己添加的一个Event,专门用来通知导航变化

TopCanGoBackEvent.java

public class TopCanGoBackEvent extends Event<TopCanGoBackEvent> {
 public static final String EVENT_NAME = "topChange"; 
 private WritableMap mEventData;
 public TopCanGoBackEvent(int viewId, WritableMap eventData) {
  super(viewId);
  mEventData = eventData;
 }
 @Override
 public String getEventName() {
  return EVENT_NAME;
 }
 @Override
 public boolean canCoalesce() {
  return false;
 }
 @Override
 public short getCoalescingKey() {
  // All events for a given view can be coalesced.
  return 0;
 }
 @Override
 public void dispatch(RCTEventEmitter rctEventEmitter) {
  rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData);
 }
}

新建 ReactWebViewPage.java

public class ReactWebViewPackage implements ReactPackage {
  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }
  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Arrays.<ViewManager>asList(
        new ReactWebViewManager()
    );
  }
}

然后在MainApplication中添加这个模块

public class MainApplication extends Application implements ReactApplication {
  @Override
  protected List<ReactPackage> getPackages() {
   return Arrays.<ReactPackage>asList(
     new MainReactPackage(),
     new ReactWebViewPackage()  //WebView
   );
  }
}

以上就是Android需要修改的地方,ios我没有尝试过,应该大差不差同一个道理。

2. 拷贝下图中的文件到我们自己项目中的JS代码目录下,并修改一下名字

JS代码目录:

CustomWebView.android.js 有几个地方需要修改。

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule CustomWebView  //此处需要修改名称
 */
var RCT_WEBVIEW_REF = 'webview1'; //此处需要修改名称
 render() {
  var webView =
   <NativeWebView
    onLoadingStart={this.onLoadingStart}
    onLoadingFinish={this.onLoadingFinish}
    onLoadingError={this.onLoadingError}
    onChange={this.onChange} //添加方法
   />;
  return (
   <View style={styles.container}>
    {webView}
    {otherView}
   </View>
  );
 }
 onChange = (event) => {  //添加方法
  this.updateNavigationState(event);
 };
}
var RCTWebView = requireNativeComponent('RCTWebView1', CustomWebView, CustomWebView.extraNativeComponentConfig); //修改名称
module.exports = CustomWebView; //修改名称

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

JS怎样操作改变radio的状态

JS脚本加载后再执行相应回调函数的操作

The above is the detailed content of How to solve the return processing of react-native WebView without calling it. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn