React-native WebView return processing (non-callback method can be solved ),

Source: Internet
Author: User

React-native WebView return processing (non-callback method can be solved ),

1. Preface

Some page content in the project is frequently changed. We will consider using webpages to solve these pages.

A public web page is provided in the RN project. If the webpage content is displayed, the page is displayed.

At this point, a problem occurs: The webpage has a level-1 page and level-2 page, which will be designed to process the return key in the navigation bar (and process the return key on Android ).

This problem can be solved on the RN official website. The onNavigationStateChange callback method is used to record the current navigation status, so as to determine whether the previous page is returned or exited, and return to other interfaces of the App.

However, when the implementation of the web page is React, there will be problems. You will find that when the page jumps, the onNavigationStateChange callback method has no callback !!! Fat four !!

At the beginning, I tried to replace my webpage address with Baidu address, and I could receive Callbacks. Everything was running very well, but I couldn't switch to our link, so I threw the pot to the background, which side of React is incorrect.

Because the previous project was too time-consuming and didn't have time to take a good look at the source code, I thought of a not-perfect solution, that is, the webpage uses js to call the App back and forth to tell the current navigation status, this solution is unfriendly.

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

2. Cause

Next we will analyze the cause of this problem and my solution.

1. First, find the source code.

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

Node_modules \ react-native \ Libraries \ Components \ WebView

The directory structure is as follows:

 

2. Implemented code segment (JAVA side)

The actual running code of rn is all native code. Therefore, some event callbacks of WebView components are actually triggered by callback in native code. As follows:

(ReactWebViewManager. java) rn version 0.47.1

Protected static class ReactWebViewClient extends WebViewClient {// WebViewClient is a tool used to listen to webpage loading when writing Android native code. Protected static final String REACT_CLASS = "RCTWebView"; // native component name defined, which will be matched in the subsequent JS. //... @ Override public void onPageStarted (WebView webView, String url, Bitmap favicon) {// There are many callback methods. Here we only use super. onPageStarted (webView, url, favicon); mLastLoadFailed = false; dispatchEvent (webView, new TopLoadingStartEvent (// custom time, after dispatch, the event will be passed to js webView. getId (), createWebViewEvent (webView, url )));}//...}

(ReactWebViewManager. java) different versions of rn 0.43.3 and RN have code adjustments. Therefore, we need to perform regression tests carefully when upgrading RN.

Protected static class ReactWebViewClient extends WebViewClient {// WebViewClient is a tool used to listen to webpage loading when writing Android native code. Protected static final String REACT_CLASS = "RCTWebView"; // native component name defined, which will be matched in the subsequent JS. //... @ Override public void onPageStarted (WebView webView, String url, Bitmap favicon) {// There are many callback methods. Here we only use super. onPageStarted (webView, url, favicon); mLastLoadFailed = false; dispatchEvent (webView, new TopLoadingStartEvent (// custom time, after dispatch, the event will be passed to js webView. getId (), createWebViewEvent (webView, url) ;}@ Override public void doUpdateVisitedHistory (WebView webView, String url, boolean isReload) {// This is the case, here, when the navigation changes, the callback will be processed in this version, but I don't know which version to delete -. -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"; // The method is onLoadingStart, because you are not familiar with the structure of RN, it took a long time to study how the file was matched, and finally found the file 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)

This file defines the corresponding relationship

/** * 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 end)

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

In the following code, we can see that only onLoadingStart and onLoadingFinish can call updateNavigationState. This problem occurs. Because our webpage implements React, there is only one page! Therefore, onLoadingStart and onLoadingFinish are called only once. Clicking the details page will not jump to the new page, but refresh the original page. Therefore, there is no updateNavigationState callback.

Class WebView extends React. component {static propTypes = {// configurable attributes that can be defined externally... viewPropTypes, renderError: PropTypes. func, renderLoading: PropTypes. func, onLoad: PropTypes. func ,//...} render () {// draw the page content //... 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, {// corresponds to 'rctwebview' nativeOnly: {messagingEnabled: PropTypes. bool,},}); module. exports = WebView;

2. Solution

Since the cause is found, it is easy to solve it.

Solution: Customize WebView, add doUpdateVisitedHistory, and notify JS every time the navigation changes.

1. copy the files to the Android code directory of our project.

The copied Android directory:

ReactWebViewManager. java needs to be modified in a few places

Public class ReactWebViewManager extends SimpleViewManager <WebView> {protected static final String REACT_CLASS = "RCTWebView1 "; // modify the name protected static class ReactWebViewClient extends WebViewClient {@ Override public void doUpdateVisitedHistory (WebView webView, String url, boolean isReload) {super. doUpdateVisitedHistory (webView, url, isReload); dispatchEvent (// when the navigation changes, dispatchEvent webView, new TopCanGoBackEvent (webView. getId (), createCanGoBackWebViewEvent (webView, url )));}}}

TopCanGoBackEvent is an Event added by myself. It is used to notify navigation changes.

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); }}

Create 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()    );  }}

Then add this module to MainApplication.

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

The above is Where Android needs to be modified. I have never tried ios, so the difference should be the same.

2. copy the file to the JS Code directory of our project and modify the name.

JS Code directory:

CustomWebView. android. js needs to be modified in a few places.

/*** 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 // you need to modify the name */var RCT_WEBVIEW_REF = 'webview1'. // you need to modify the name render () {var webView = <NativeWebView onLoadingStart = {this. onLoadingStart} onLoadingFinish = {this. onLoadingFinish} onLoadingError = {this. onLoadingError} onChange = {this. onChange} // Add method/>; return (<View style = {styles. container >{webview }{ otherView} </View>);} onChange = (event) =>{// add method this. updateNavigationState (event) ;};} var RCTWebView = requireNativeComponent ('rctwebview1', CustomWebView, CustomWebView. extraNativeComponentConfig); // modify the name module. exports = CustomWebView; // modify the name

This completes the custom WebView module. It can also solve the problem that web pages are implemented by React and cannot be navigated.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.