Browse Source

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

iou90 6 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
 }
47
 }
48
 
48
 
49
 function getReloadRelatedData(props) {
49
 function getReloadRelatedData(props) {
50
-  const { hasIframe, files, customStyle, customScript, style } = props;
50
+  const { hasIframe, files, customStyle, customScript, style, source } = props;
51
   return {
51
   return {
52
+    source,
52
     hasIframe,
53
     hasIframe,
53
     files,
54
     files,
54
     customStyle,
55
     customStyle,
71
   return script;
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
   } else {
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
 function handleSizeUpdated(height, width, onSizeUpdated) {
126
 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
 const domMutationObserveScript = `
147
 const domMutationObserveScript = `
102
 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
148
 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
103
 var observer = new MutationObserver(updateSize);
149
 var observer = new MutationObserver(updateSize);
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
 
6
 
7
 import PropTypes from 'prop-types';
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
 export default class AutoHeightWebView extends PureComponent {
13
 export default class AutoHeightWebView extends PureComponent {
12
   static propTypes = {
14
   static propTypes = {
13
     hasIframe: PropTypes.bool,
15
     hasIframe: PropTypes.bool,
16
+    onMessage: PropTypes.func,
14
     source: WebView.propTypes.source,
17
     source: WebView.propTypes.source,
15
     customScript: PropTypes.string,
18
     customScript: PropTypes.string,
16
     customStyle: PropTypes.string,
19
     customStyle: PropTypes.string,
52
     super(props);
55
     super(props);
53
     const { enableAnimation, style } = props;
56
     const { enableAnimation, style } = props;
54
     enableAnimation && (this.opacityAnimatedValue = new Animated.Value(0));
57
     enableAnimation && (this.opacityAnimatedValue = new Animated.Value(0));
58
+    this.webView = React.createRef();
55
     this.state = {
59
     this.state = {
56
-      isScriptChanged: false,
57
       width: getWidth(style),
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
   handleNavigationStateChange = navState => {
67
   handleNavigationStateChange = navState => {
71
     const { title } = navState;
68
     const { title } = navState;
77
     const height = Number(heightValue);
74
     const height = Number(heightValue);
78
     const { height: oldHeight, width: oldWidth } = this.state;
75
     const { height: oldHeight, width: oldWidth } = this.state;
79
     if ((height && height !== oldHeight) || (width && width !== oldWidth)) {
76
     if ((height && height !== oldHeight) || (width && width !== oldWidth)) {
80
-      // if ((height && height !== oldHeight)) {
81
       const { enableAnimation, animationDuration, onSizeUpdated } = this.props;
77
       const { enableAnimation, animationDuration, onSizeUpdated } = this.props;
82
       enableAnimation && this.opacityAnimatedValue.setValue(0);
78
       enableAnimation && this.opacityAnimatedValue.setValue(0);
79
+      this.updatingSize = true;
83
       this.setState(
80
       this.setState(
84
         {
81
         {
85
           height,
82
           height,
86
           width
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
   stopLoading() {
103
   stopLoading() {
103
     this.webView.stopLoading();
104
     this.webView.stopLoading();
104
   }
105
   }
105
 
106
 
106
   render() {
107
   render() {
107
-    const { height, width, script } = this.state;
108
+    const { height, width } = this.state;
108
     const {
109
     const {
110
+      onMessage,
109
       onError,
111
       onError,
110
       onLoad,
112
       onLoad,
111
       onLoadStart,
113
       onLoadStart,
113
       onShouldStartLoadWithRequest,
115
       onShouldStartLoadWithRequest,
114
       scalesPageToFit,
116
       scalesPageToFit,
115
       enableAnimation,
117
       enableAnimation,
116
-      source,
117
       heightOffset,
118
       heightOffset,
118
       style,
119
       style,
119
       scrollEnabled
120
       scrollEnabled
120
     } = this.props;
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
     return (
129
     return (
127
       <Animated.View
130
       <Animated.View
128
         style={[
131
         style={[
129
           styles.container,
132
           styles.container,
130
           {
133
           {
131
             opacity: enableAnimation ? this.opacityAnimatedValue : 1,
134
             opacity: enableAnimation ? this.opacityAnimatedValue : 1,
132
-            width,
133
-            height: height + heightOffset
135
+            width: w,
136
+            height: h + heightOffset
134
           },
137
           },
135
           style
138
           style
136
         ]}
139
         ]}
137
       >
140
       >
138
         <WebView
141
         <WebView
139
           originWhitelist={['*']}
142
           originWhitelist={['*']}
140
-          ref={this.getWebView}
143
+          ref={this.webView}
144
+          onMessage={onMessage}
141
           onError={onError}
145
           onError={onError}
142
           onLoad={onLoad}
146
           onLoad={onLoad}
143
           onLoadStart={onLoadStart}
147
           onLoadStart={onLoadStart}
144
           onLoadEnd={onLoadEnd}
148
           onLoadEnd={onLoadEnd}
145
           onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
149
           onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
146
           style={styles.webView}
150
           style={styles.webView}
147
-          injectedJavaScript={script}
148
           scrollEnabled={!!scrollEnabled}
151
           scrollEnabled={!!scrollEnabled}
149
           scalesPageToFit={scalesPageToFit}
152
           scalesPageToFit={scalesPageToFit}
150
-          source={webViewSource}
153
+          injectedJavaScript={script}
154
+          source={source}
151
           onNavigationStateChange={this.handleNavigationStateChange}
155
           onNavigationStateChange={this.handleNavigationStateChange}
152
         />
156
         />
153
       </Animated.View>
157
       </Animated.View>

+ 22
- 0
autoHeightWebView/momoize.js View File

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
+}