Browse Source

add memoize; apply memoize for common & index.ios; add onMessage to index.ios

iou90 5 years ago
parent
commit
38ef16302b
3 changed files with 118 additions and 46 deletions
  1. 61
    15
      autoHeightWebView/common.js
  2. 35
    31
      autoHeightWebView/index.ios.js
  3. 22
    0
      autoHeightWebView/momoize.js

+ 61
- 15
autoHeightWebView/common.js View File

@@ -47,8 +47,9 @@ function appendStylesToHead(styles, script) {
47 47
 }
48 48
 
49 49
 function getReloadRelatedData(props) {
50
-  const { hasIframe, files, customStyle, customScript, style } = props;
50
+  const { hasIframe, files, customStyle, customScript, style, source } = props;
51 51
   return {
52
+    source,
52 53
     hasIframe,
53 54
     files,
54 55
     customStyle,
@@ -71,23 +72,55 @@ function getScript(props, getBaseScript, getIframeBaseScript) {
71 72
   return script;
72 73
 }
73 74
 
74
-function getSize(nextStyle, prevStyle) {
75
-  if (!nextStyle) {
76
-    return null;
77
-  }
78
-  if (isChanged(nextStyle, prevStyle)) {
79
-    let size = null;
80
-    const { height, width } = nextStyle;
81
-    height && Object.assign(size, { height });
82
-    width && Object.assign(size, { width });
83
-    return size;
75
+function isEqual(newProps, oldProps) {
76
+  return isChanged(getReloadRelatedData(newProps), getReloadRelatedData(oldProps));
77
+}
78
+
79
+function insertStringAfterAnotherString(raw, searchValue, insertValue) {
80
+  const position = raw.indexOf(searchValue) + searchValue.length;
81
+  return [raw.slice(0, position), insertValue, raw.slice(position)].join('');
82
+}
83
+
84
+function getInjectedSource(html, script) {
85
+  const scriptString = `
86
+  <script>
87
+  ${script}
88
+  </script>
89
+  `;
90
+  if (html.startsWith('<html')) {
91
+    return insertStringAfterAnotherString(html, '>', scriptString);
84 92
   } else {
85
-    return null;
93
+    return `
94
+    ${html}
95
+    ${scriptString}
96
+    `;
86 97
   }
87 98
 }
88 99
 
89
-function isScriptChanged(nextProps, props) {
90
-  return nextProps && props && isChanged(getReloadRelatedData(nextProps), getReloadRelatedData(props));
100
+function setState(props, getBaseScript, getIframeBaseScript) {
101
+  const { source, style } = props;
102
+  const script = getScript(props, getBaseScript, getIframeBaseScript);
103
+  let state = {
104
+    height: style && style.height ? style.height : 0,
105
+    width: getWidth(style)
106
+  };
107
+  if (source.html) {
108
+    Object.assign(state, {
109
+      source: Object.assign(
110
+        {},
111
+        {
112
+          html: getInjectedSource(source.html, script),
113
+          baseUrl: 'web/'
114
+        }
115
+      )
116
+    });
117
+  } else {
118
+    Object.assign(state, {
119
+      source: Object.assign({}, source, { baseUrl: 'web/' }),
120
+      script
121
+    });
122
+  }
123
+  return state;
91 124
 }
92 125
 
93 126
 function handleSizeUpdated(height, width, onSizeUpdated) {
@@ -98,6 +131,19 @@ function handleSizeUpdated(height, width, onSizeUpdated) {
98 131
     });
99 132
 }
100 133
 
134
+function getSize(newHeight, newWidth, height, width, updatingSize, calledOnce) {
135
+  if (!calledOnce || updatingSize) {
136
+    return {
137
+      h: height,
138
+      w: width
139
+    };
140
+  }
141
+  return {
142
+    h: height,
143
+    w: width
144
+  };
145
+}
146
+
101 147
 const domMutationObserveScript = `
102 148
 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
103 149
 var observer = new MutationObserver(updateSize);
@@ -107,4 +153,4 @@ observer.observe(document, {
107 153
 });
108 154
 `;
109 155
 
110
-export { getSize, isScriptChanged, getWidth, getScript, handleSizeUpdated, domMutationObserveScript };
156
+export { isEqual, setState, getWidth, handleSizeUpdated, domMutationObserveScript, getSize };

+ 35
- 31
autoHeightWebView/index.ios.js View File

@@ -6,11 +6,14 @@ import { Animated, StyleSheet, ViewPropTypes, WebView } from 'react-native';
6 6
 
7 7
 import PropTypes from 'prop-types';
8 8
 
9
-import { isScriptChanged, getSize, getWidth, getScript, handleSizeUpdated, domMutationObserveScript } from './common.js';
9
+import { getSize, isEqual, setState, getWidth, handleSizeUpdated, domMutationObserveScript } from './common.js';
10
+
11
+import momoize from './momoize';
10 12
 
11 13
 export default class AutoHeightWebView extends PureComponent {
12 14
   static propTypes = {
13 15
     hasIframe: PropTypes.bool,
16
+    onMessage: PropTypes.func,
14 17
     source: WebView.propTypes.source,
15 18
     customScript: PropTypes.string,
16 19
     customStyle: PropTypes.string,
@@ -52,20 +55,14 @@ export default class AutoHeightWebView extends PureComponent {
52 55
     super(props);
53 56
     const { enableAnimation, style } = props;
54 57
     enableAnimation && (this.opacityAnimatedValue = new Animated.Value(0));
58
+    this.webView = React.createRef();
55 59
     this.state = {
56
-      isScriptChanged: false,
57 60
       width: getWidth(style),
58
-      height: style && style.height ? style.height : 0,
59
-      script: getScript(props, getBaseScript, getIframeBaseScript)
61
+      height: style && style.height ? style.height : 0
60 62
     };
61 63
   }
62 64
 
63
-  componentWillReceiveProps(nextProps) {
64
-    const size = getSize(nextProps, this.props);
65
-    size && this.setState(size); 
66
-    this.isScriptChanged = isScriptChanged(nextProps, this.props);
67
-    this.isScriptChanged && this.setState({ script: getScript(nextProps, getBaseScript, getIframeBaseScript) });
68
-  }
65
+  getUpdatedState = momoize(setState, isEqual);
69 66
 
70 67
   handleNavigationStateChange = navState => {
71 68
     const { title } = navState;
@@ -77,35 +74,40 @@ export default class AutoHeightWebView extends PureComponent {
77 74
     const height = Number(heightValue);
78 75
     const { height: oldHeight, width: oldWidth } = this.state;
79 76
     if ((height && height !== oldHeight) || (width && width !== oldWidth)) {
80
-      // if ((height && height !== oldHeight)) {
81 77
       const { enableAnimation, animationDuration, onSizeUpdated } = this.props;
82 78
       enableAnimation && this.opacityAnimatedValue.setValue(0);
79
+      this.updatingSize = true;
83 80
       this.setState(
84 81
         {
85 82
           height,
86 83
           width
87 84
         },
88 85
         () => {
89
-          enableAnimation
90
-            ? Animated.timing(this.opacityAnimatedValue, {
91
-                toValue: 1,
92
-                duration: animationDuration
93
-              }).start(() => handleSizeUpdated(height, width, onSizeUpdated))
94
-            : handleSizeUpdated(height, width, onSizeUpdated);
86
+          if (enableAnimation) {
87
+            Animated.timing(this.opacityAnimatedValue, {
88
+              toValue: 1,
89
+              duration: animationDuration
90
+            }).start(() => {
91
+              handleSizeUpdated(height, width, onSizeUpdated);
92
+              this.updatingSize = false;
93
+            });
94
+          } else {
95
+            handleSizeUpdated(height, width, onSizeUpdated);
96
+            this.updatingSize = false;
97
+          }
95 98
         }
96 99
       );
97 100
     }
98 101
   };
99 102
 
100
-  getWebView = webView => (this.webView = webView);
101
-
102 103
   stopLoading() {
103 104
     this.webView.stopLoading();
104 105
   }
105 106
 
106 107
   render() {
107
-    const { height, width, script } = this.state;
108
+    const { height, width } = this.state;
108 109
     const {
110
+      onMessage,
109 111
       onError,
110 112
       onLoad,
111 113
       onLoadStart,
@@ -113,41 +115,43 @@ export default class AutoHeightWebView extends PureComponent {
113 115
       onShouldStartLoadWithRequest,
114 116
       scalesPageToFit,
115 117
       enableAnimation,
116
-      source,
117 118
       heightOffset,
118 119
       style,
119 120
       scrollEnabled
120 121
     } = this.props;
121
-    let webViewSource = Object.assign({}, source, { baseUrl: 'web/' });
122
-    if (this.isScriptChanged) {
123
-      this.changeSourceFlag = !this.changeSourceFlag;
124
-      webViewSource = Object.assign(webViewSource, { changeSourceFlag: this.changeSourceFlag });
125
-    }
122
+    const { height: newHeight, width: newWidth, source, script } = this.getUpdatedState(
123
+      this.props,
124
+      getBaseScript,
125
+      getIframeBaseScript
126
+    );
127
+    const { w, h } = getSize(newHeight, newWidth, height, width, this.updatingSize, this.calledOnce);
128
+    this.calledOnce = true;
126 129
     return (
127 130
       <Animated.View
128 131
         style={[
129 132
           styles.container,
130 133
           {
131 134
             opacity: enableAnimation ? this.opacityAnimatedValue : 1,
132
-            width,
133
-            height: height + heightOffset
135
+            width: w,
136
+            height: h + heightOffset
134 137
           },
135 138
           style
136 139
         ]}
137 140
       >
138 141
         <WebView
139 142
           originWhitelist={['*']}
140
-          ref={this.getWebView}
143
+          ref={this.webView}
144
+          onMessage={onMessage}
141 145
           onError={onError}
142 146
           onLoad={onLoad}
143 147
           onLoadStart={onLoadStart}
144 148
           onLoadEnd={onLoadEnd}
145 149
           onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
146 150
           style={styles.webView}
147
-          injectedJavaScript={script}
148 151
           scrollEnabled={!!scrollEnabled}
149 152
           scalesPageToFit={scalesPageToFit}
150
-          source={webViewSource}
153
+          injectedJavaScript={script}
154
+          source={source}
151 155
           onNavigationStateChange={this.handleNavigationStateChange}
152 156
         />
153 157
       </Animated.View>

+ 22
- 0
autoHeightWebView/momoize.js View File

@@ -0,0 +1,22 @@
1
+'use strict';
2
+
3
+function defaultIsNewArgEqualToLast(newArgs, lastArgs) {
4
+    return newArgs.length === lastArgs.length && newArgs.every((arg, index) => arg === lastArgs[index]);
5
+}
6
+
7
+export default function memoize(resultCallback, isNewArgEqualToLast) {
8
+  let lastArgs = [];
9
+  let lastResult;
10
+  let calledOnce = false;
11
+  const isEqual = isNewArgEqualToLast || defaultIsNewArgEqualToLast;
12
+  const result = function(...newArgs) {
13
+    if (calledOnce && isEqual(newArgs, lastArgs)) {
14
+      return lastResult;
15
+    }
16
+    calledOnce = true;
17
+    lastArgs = newArgs;
18
+    lastResult = resultCallback.apply(this, newArgs);
19
+    return lastResult;
20
+  };
21
+  return result;
22
+}