Repository: infernojs/inferno
Pull request #1180 (2017-07-30 22:22:48)

do not rerender same innerHTML
**Objective**

do not rerender same innerHTML

**Closes Issue**

It closes Issue #1177



Patch:
diff --git a/README.md b/README.md
index cf65c61ad..d029866ef 100755
--- a/README.md
+++ b/README.md
@@ -525,6 +525,7 @@ Inferno supports Edge, Chrome, Firefox and Safari 8+. In order to support IE8-11
 - [Map object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
 - [WeakMap object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap)
 - [Object.keys](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)
+- [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)
 
 Potential solutions include using the [es5-shim](https://github.com/es-shims/es5-shim) for ES5 features and [es6-shim](https://github.com/paulmillr/es6-shim) from ES2015 features.
 
diff --git a/benchmarks/async-render/index.html b/benchmarks/async-render/index.html
index 062f845a0..0f2145cfe 100644
--- a/benchmarks/async-render/index.html
+++ b/benchmarks/async-render/index.html
@@ -7,7 +7,6 @@
 </head>
 <body>
 <div id="App"></div>
-<script src="https://localvoid.github.io/uibench-base/0.1.0/uibench.js"></script>
 <script src="../../packages/inferno/dist/inferno.js"></script>
 <script src="../../packages/inferno-create-element/dist/inferno-create-element.js"></script>
 <script src="../../packages/inferno-component/dist/inferno-component.js"></script>
diff --git a/benchmarks/events-bench/app.js b/benchmarks/events-bench/app.js
new file mode 100644
index 000000000..d4e1b69cd
--- /dev/null
+++ b/benchmarks/events-bench/app.js
@@ -0,0 +1,103 @@
+(function() {
+  "use strict";
+  /* This benchmark tests only how quickly Inferno delegates through Synthetic events
+
+  - create long nested tree
+  - trigger event from the bottom (button)
+  - measure the time it takes to finish the delegation loop
+
+	*/
+  var createElement = Inferno.createElement;
+  var counter = 0;
+
+  function App() {
+    var divs = [];
+    var first = createElement("div", null, divs);
+    var content = [first];
+
+    for (var i = 0; i < 500; i++) {
+      if (i % 3 === 0) {
+        divs.push(createElement("div", null, []));
+        first = divs[divs.length - 1];
+        divs = first.children;
+      }
+
+      divs.push(
+        createElement("div", {
+          className: "foobar",
+          onDblClick: function() {
+            counter++;
+          },
+          onClick: function() {
+            counter--;
+          }
+        })
+      );
+
+      if (i === 499) {
+        divs[divs.length - 1].children = createElement(
+          "button",
+          {
+            onClick: startBench
+          },
+          "START!"
+        );
+      }
+    }
+
+    return createElement(
+      "div",
+      {
+        onClick: function() {
+          counter++;
+        }
+      },
+      content
+    );
+  }
+
+  var count = 100;
+  var totalTime = 0;
+
+  function renderResults() {
+    Inferno.render(
+      createElement(
+        "div",
+        null,
+        `
+			Rounds: ${count},
+			Average: ${totalTime / count},
+			Total: ${totalTime}
+		`
+      ),
+      document.querySelector("#results")
+    );
+    totalTime = 0;
+  }
+
+  function startBench() {
+    var times = [];
+
+    var button = document.querySelector("button");
+
+    for (var i = 0; i < count; i++) {
+      var start = window.performance.now();
+
+      button.click();
+
+      var end = window.performance.now();
+
+      var roundTime = end - start;
+      totalTime += roundTime;
+
+      times.push(roundTime);
+    }
+
+    renderResults();
+  }
+
+  document.addEventListener("DOMContentLoaded", function() {
+    var container = document.querySelector("#App");
+    Inferno.render(createElement(App), container);
+  });
+})();
diff --git a/benchmarks/events-bench/index.html b/benchmarks/events-bench/index.html
new file mode 100644
index 000000000..a03c80130
--- /dev/null
+++ b/benchmarks/events-bench/index.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <link href="https://localvoid.github.io/uibench-base/0.1.0/styles.css" rel="stylesheet" />
+    <title>Events bench: Inferno</title>
+    <style>
+        button {
+            position: fixed;
+            top: 1rem;
+            left: 1rem;
+            z-index: 1;
+        }
+    </style>
+</head>
+<body>
+<div id="results"></div>
+<div id="App"></div>
+<script src="../../packages/inferno/dist/inferno.js"></script>
+<script src="../../packages/inferno-create-element/dist/inferno-create-element.js"></script>
+<script src="app.js" type="text/javascript"></script>
+</body>
+</html>
diff --git a/lerna.json b/lerna.json
index 3dc38bac0..88d46068a 100644
--- a/lerna.json
+++ b/lerna.json
@@ -8,5 +8,5 @@
   "packages": [
     "packages/*"
   ],
-  "version": "3.7.0"
+  "version": "3.7.1"
 }
diff --git a/package.json b/package.json
index 3b9e11185..b490d0a4d 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "name": "inferno-build",
   "private": true,
   "license": "MIT",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "author": {
     "name": "Dominic Gannaway",
     "email": "dg@domgan.com"
@@ -70,9 +70,9 @@
   },
   "devDependencies": {
     "@types/history": "^4.6.0",
-    "@types/jest": "^20.0.4",
-    "@types/lodash": "^4.14.70",
-    "@types/node": "^8.0.14",
+    "@types/jest": "^20.0.6",
+    "@types/lodash": "^4.14.71",
+    "@types/node": "^8.0.19",
     "babel-cli": "^6.24.1",
     "babel-core": "^6.25.0",
     "babel-jest": "^20.0.3",
@@ -93,14 +93,14 @@
     "cli-table": "^0.3.1",
     "concat-stream-es6": "0.0.1",
     "coveralls": "^2.13.1",
-    "cross-env": "^5.0.1",
+    "cross-env": "^5.0.3",
     "dts-bundle": "^0.7.3",
     "history": "^4.6.3",
     "jest": "^20.0.4",
     "lerna": "^2.0.0",
     "lint-staged": "^4.0.2",
     "minimist": "^1.2.0",
-    "most": "^1.5.0",
+    "most": "^1.5.1",
     "most-subject": "^5.3.0",
     "npm-run-all": "^4.0.2",
     "optimize-js": "^1.0.3",
@@ -110,18 +110,18 @@
     "rollup": "0.45.2",
     "rollup-plugin-alias": "^1.3.1",
     "rollup-plugin-buble": "^0.15.0",
-    "rollup-plugin-commonjs": "^8.0.2",
+    "rollup-plugin-commonjs": "^8.1.0",
     "rollup-plugin-node-builtins": "^2.1.2",
     "rollup-plugin-node-globals": "^1.1.0",
     "rollup-plugin-node-resolve": "^3.0.0",
     "rollup-plugin-replace": "^1.1.1",
     "rollup-plugin-typescript2": "^0.4.6",
     "rollup-plugin-uglify": "^2.0.1",
-    "sinon": "^2.3.8",
+    "sinon": "^3.0.0",
     "ts-jest": "^20.0.7",
     "tslint": "^5.5.0",
-    "tslint-config-prettier": "^1.1.0",
-    "typedoc": "^0.7.1",
+    "tslint-config-prettier": "^1.3.0",
+    "typedoc": "^0.8.0",
     "typedoc-plugin-external-module-name": "^1.0.9",
     "typescript": "^2.4.2",
     "union-type-es": "^0.1.0"
diff --git a/packages/inferno-compat/package.json b/packages/inferno-compat/package.json
index 50d3d981c..e2214d20d 100644
--- a/packages/inferno-compat/package.json
+++ b/packages/inferno-compat/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-compat",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides a compatibility with React codebases",
   "author": {
@@ -23,17 +23,17 @@
     "user interfaces"
   ],
   "dependencies": {
-    "inferno": "3.7.0",
-    "inferno-component": "3.7.0",
-    "inferno-create-class": "3.7.0",
-    "inferno-create-element": "3.7.0",
-    "inferno-shared": "3.7.0",
+    "inferno": "3.7.1",
+    "inferno-component": "3.7.1",
+    "inferno-create-class": "3.7.1",
+    "inferno-create-element": "3.7.1",
+    "inferno-shared": "3.7.1",
     "inferno-transition-group": "2.1.1",
-    "inferno-vnode-flags": "3.7.0",
+    "inferno-vnode-flags": "3.7.1",
     "rc-css-transition-group-modern": "^2.1.6"
   },
   "devDependencies": {
-    "inferno-hyperscript": "3.7.0"
+    "inferno-hyperscript": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno-component/package.json b/packages/inferno-component/package.json
index eb904a516..7089241b4 100644
--- a/packages/inferno-component/package.json
+++ b/packages/inferno-component/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-component",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides ES2015 stateful components (with lifecycle events) to Inferno",
   "author": {
@@ -29,9 +29,9 @@
     "rollup"
   ],
   "dependencies": {
-    "inferno": "3.7.0",
-    "inferno-shared": "3.7.0",
-    "inferno-vnode-flags": "3.7.0"
+    "inferno": "3.7.1",
+    "inferno-shared": "3.7.1",
+    "inferno-vnode-flags": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno-create-class/package.json b/packages/inferno-create-class/package.json
index 88f12ad8f..7cdfdd930 100644
--- a/packages/inferno-create-class/package.json
+++ b/packages/inferno-create-class/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-create-class",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides a helper to create Inferno Components without needing ES2015",
   "author": {
@@ -23,12 +23,12 @@
     "user interfaces"
   ],
   "dependencies": {
-    "inferno-component": "3.7.0",
-    "inferno-shared": "3.7.0"
+    "inferno-component": "3.7.1",
+    "inferno-shared": "3.7.1"
   },
   "devDependencies": {
-    "inferno": "3.7.0",
-    "inferno-create-element": "3.7.0"
+    "inferno": "3.7.1",
+    "inferno-create-element": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno-create-element/package.json b/packages/inferno-create-element/package.json
index 8b7b060f5..7ade50a86 100644
--- a/packages/inferno-create-element/package.json
+++ b/packages/inferno-create-element/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-create-element",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides methods to create Inferno VNodes",
   "author": {
@@ -23,10 +23,10 @@
     "user interfaces"
   ],
   "dependencies": {
-    "inferno": "3.7.0",
-    "inferno-component": "3.7.0",
-    "inferno-shared": "3.7.0",
-    "inferno-vnode-flags": "3.7.0"
+    "inferno": "3.7.1",
+    "inferno-component": "3.7.1",
+    "inferno-shared": "3.7.1",
+    "inferno-vnode-flags": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno-devtools/package.json b/packages/inferno-devtools/package.json
index 60c54578c..5d348859e 100644
--- a/packages/inferno-devtools/package.json
+++ b/packages/inferno-devtools/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-devtools",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides support for React's Dev Tools for Inferno",
   "author": {
@@ -24,10 +24,10 @@
   ],
   "repository": "https://github.com/infernojs/inferno",
   "dependencies": {
-    "inferno": "3.7.0",
-    "inferno-component": "3.7.0",
-    "inferno-shared": "3.7.0",
-    "inferno-vnode-flags": "3.7.0"
+    "inferno": "3.7.1",
+    "inferno-component": "3.7.1",
+    "inferno-shared": "3.7.1",
+    "inferno-vnode-flags": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno-hyperscript/package.json b/packages/inferno-hyperscript/package.json
index d34de5dee..acd0424e5 100644
--- a/packages/inferno-hyperscript/package.json
+++ b/packages/inferno-hyperscript/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-hyperscript",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Bridging hyperscript to InfernoJS",
   "author": "Terin Stock <terinjokes@gmail.com> (https://terinstock.com/)",
@@ -17,9 +17,9 @@
   ],
   "repository": "https://github.com/infernojs/inferno",
   "dependencies": {
-    "inferno": "3.7.0",
-    "inferno-shared": "3.7.0",
-    "inferno-vnode-flags": "3.7.0"
+    "inferno": "3.7.1",
+    "inferno-shared": "3.7.1",
+    "inferno-vnode-flags": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno-mobx/package.json b/packages/inferno-mobx/package.json
index d2f84ef23..36eafa468 100644
--- a/packages/inferno-mobx/package.json
+++ b/packages/inferno-mobx/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-mobx",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Official Inferno bindings for Mobx",
   "author": {
@@ -27,11 +27,11 @@
   "repository": "https://github.com/infernojs/inferno",
   "dependencies": {
     "hoist-non-inferno-statics": "^1.1.3",
-    "inferno": "3.7.0",
-    "inferno-component": "3.7.0",
-    "inferno-create-class": "3.7.0",
-    "inferno-create-element": "3.7.0",
-    "inferno-shared": "3.7.0",
+    "inferno": "3.7.1",
+    "inferno-component": "3.7.1",
+    "inferno-create-class": "3.7.1",
+    "inferno-create-element": "3.7.1",
+    "inferno-shared": "3.7.1",
     "mobx": "*"
   },
   "rollup": {
diff --git a/packages/inferno-redux/package.json b/packages/inferno-redux/package.json
index b40e99275..149b82595 100644
--- a/packages/inferno-redux/package.json
+++ b/packages/inferno-redux/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-redux",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Official Inferno bindings for Redux",
   "author": {
@@ -26,15 +26,15 @@
   "repository": "https://github.com/infernojs/inferno",
   "dependencies": {
     "hoist-non-inferno-statics": "^1.1.3",
-    "inferno": "3.7.0",
-    "inferno-component": "3.7.0",
-    "inferno-create-element": "3.7.0",
-    "inferno-shared": "3.7.0",
+    "inferno": "3.7.1",
+    "inferno-component": "3.7.1",
+    "inferno-create-element": "3.7.1",
+    "inferno-shared": "3.7.1",
     "lodash.isplainobject": "^4.0.6",
     "redux": "^2.0.0 || ^3.0.0"
   },
   "devDependencies": {
-    "inferno-create-class": "3.7.0"
+    "inferno-create-class": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno-router/README.md b/packages/inferno-router/README.md
index 5f4f4dd56..16976f31a 100644
--- a/packages/inferno-router/README.md
+++ b/packages/inferno-router/README.md
@@ -209,6 +209,115 @@ Inferno.render((
 </Router>
 ```
 
+## Async Data Fetching
+To mimic standard browser behavior you want to fetch data before performing a transition. To
+achieve this you can use the `asyncBefore` hooks:
+
+```js
+        <Router history={browserHistory} asyncBefore={ /* user provided handler that resolves and calls route specific asyncBefore hooks */ }>
+          <Route path="/" asyncBefore={ /* route specific asyncBefore handler */ } component={ ... } />
+        </Router>
+```
+
+You will need a data store such as Mobx or Redux to propagate data to your component.
+
+### Pages and Routing
+
+```js
+  import Component from "inferno-component";
+
+  class PageOne extends Component {
+    static fetchData(params) {
+      return myDataFetcher() // Returns a promise
+    }
+
+    render() {
+      return <div>Page One <span>{dataStore.pageOne}</span></div>;
+    }
+  }
+
+  class PageTwo extends Component {
+    static fetchData(params) {
+      return myDataFetcher() // Returns a promise
+      });
+    }
+
+    render() {
+      return (
+        <div>Page Two <span>{dataStore.pageTwo}</span>{this.props.children}</div>
+      );
+    }
+  }
+
+  import { render } from "inferno";
+  import { Route, Router, match, doAllAsyncBefore } from "inferno-router";
+
+  const routes = (
+    <Router history={...} asyncBefore={
+      url => {
+        return doAllAsyncBefore(match(routes, url));
+      }
+    }>
+      <Route path="/" asyncBefore={PageOne.fetchData} component={PageOne} />
+      <Route path="/test" asyncBefore={PageTwo.fetchData} component={PageTwo} />
+    </Router>
+  );
+```
+
+### Render in Browser Without SSR
+
+```js
+  import { render } from "inferno";
+  import { match, doAllAsyncBefore } from "inferno-router";
+
+  // Need to prime the route props with data on first render
+  const renderProps = match(routes, "/");
+  doAllAsyncBefore(renderProps).then(() => {
+    // Then render
+    render(routes, document.getElementById('app'));
+  });
+```
+
+### Render in Browser WITH SSR
+```js
+  // Rehydrate state passed from server and then render...
+  if (typeof window !== 'undefined') {
+    render(routes, document.getElementById('app'));
+  }
+```
+
+### Server Side Rendering (SSR)
+This code will be executed in your server side controller and imports routes
+from app bundle. You need to pass the hydrated state to the client and rehydrate
+it before rendering.
+
+```js
+  import { RouterContext, match, doAllAsyncBefore } from "inferno-router";
+  import InfernoServer from "inferno-server";
+  import createElement from "inferno-create-element";
+
+  const renderProps = match(routes, "/");
+  doAllAsyncBefore(renderProps).then(() => {
+    const html = InfernoServer.renderToString(
+      createElement(RouterContext, renderProps)
+    );
+    
+    // Pass hydrated state (from Mobx or Redux) and rendered html to template
+  });
+```
+
+### Lifercycle of Route Transition
+
+1. onLeave
+2. asyncBefore (Router)
+3. -> asyncBefore (Route)
+4. render route
+5. onEnter
+
+### Adding a Progress Indicator
+To add a progress indicator you would initiate and terminate it in Router.props.asyncBefore hook.
+Actual progress would be updated in the route specific asyncBefore handler.
+
 ## Notes
 
 * `<IndexRoute>` is the same as `<Route path="/">"`
diff --git a/packages/inferno-router/package.json b/packages/inferno-router/package.json
index 2f43daeea..0da4500db 100644
--- a/packages/inferno-router/package.json
+++ b/packages/inferno-router/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-router",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides routing functionality for Inferno",
   "author": {
@@ -26,11 +26,11 @@
   "repository": "https://github.com/infernojs/inferno",
   "dependencies": {
     "history": "^4.6.3",
-    "inferno": "3.7.0",
-    "inferno-component": "3.7.0",
-    "inferno-create-element": "3.7.0",
-    "inferno-shared": "3.7.0",
-    "inferno-vnode-flags": "3.7.0",
+    "inferno": "3.7.1",
+    "inferno-component": "3.7.1",
+    "inferno-create-element": "3.7.1",
+    "inferno-shared": "3.7.1",
+    "inferno-vnode-flags": "3.7.1",
     "path-to-regexp": "^1.7.0",
     "path-to-regexp-es6": "0.0.2"
   },
diff --git a/packages/inferno-router/src/Route.ts b/packages/inferno-router/src/Route.ts
index ec3a81260..d9a0753ae 100644
--- a/packages/inferno-router/src/Route.ts
+++ b/packages/inferno-router/src/Route.ts
@@ -16,6 +16,7 @@ export interface IRouteProps {
   params?: any;
   onEnter?: IRouteHook;
   onLeave?: IRouteHook;
+  asyncBefore?: any;
   path: string;
   children: Array<Component<any, any>>;
   component?: Component<any, any>;
@@ -57,6 +58,14 @@ export default class Route extends Component<IRouteProps, any> {
     });
   };
 
+  public doAsyncBefore(params) {
+    if (this.props.asyncBefore) {
+      return this.props.asyncBefore(params);
+    } else {
+      return Promise.resolve();
+    }
+  }
+
   public onLeave(trigger = false) {
     const { onLeave } = this.props;
     const { router } = this.context;
diff --git a/packages/inferno-router/src/Router.ts b/packages/inferno-router/src/Router.ts
index 7fdecf4a2..d54b6e3c2 100644
--- a/packages/inferno-router/src/Router.ts
+++ b/packages/inferno-router/src/Router.ts
@@ -15,6 +15,7 @@ export interface IRouterProps {
   location: any;
   baseUrl?: any;
   component?: Component<any, any>;
+  asyncBefore?: any;
   onUpdate?: any;
 }
 
@@ -61,7 +62,14 @@ export default class Router extends Component<IRouterProps, any> {
   public componentWillMount() {
     if (this.router) {
       this.unlisten = this.router.listen(() => {
-        this.routeTo(this.router.url);
+        if (typeof this.props.asyncBefore === "function") {
+          const self = this;
+          this.props.asyncBefore(this.router.url).then(() => {
+            self.routeTo(self.router.url);
+          });
+        } else {
+          this.routeTo(this.router.url);
+        }
       });
     }
   }
diff --git a/packages/inferno-router/src/doAllAsyncBefore.ts b/packages/inferno-router/src/doAllAsyncBefore.ts
new file mode 100644
index 000000000..8b33d7214
--- /dev/null
+++ b/packages/inferno-router/src/doAllAsyncBefore.ts
@@ -0,0 +1,21 @@
+export default function doAllAsyncBefore(renderProps) {
+  const promises: Object[] = [];
+
+  const getAsyncBefore = function(root) {
+    if (root) {
+      if (root.props && root.props.children) {
+        getAsyncBefore(root.props.children);
+      }
+
+      if (root.type.name === "Route" && root.props.asyncBefore) {
+        // Resolve asyncBefore
+        promises.push(
+          root.type.prototype.doAsyncBefore.call(root, root.props.params)
+        );
+      }
+    }
+  };
+
+  getAsyncBefore(renderProps.matched);
+  return Promise.all(promises).then(() => Promise.resolve(true));
+}
diff --git a/packages/inferno-router/src/index.ts b/packages/inferno-router/src/index.ts
index 98d9bf152..065d69169 100644
--- a/packages/inferno-router/src/index.ts
+++ b/packages/inferno-router/src/index.ts
@@ -4,6 +4,7 @@
 
 import { VNode } from "inferno";
 import createRoutes, { IPlainRouteConfig } from "./createRoutes";
+import doAllAsyncBefore from "./doAllAsyncBefore";
 import IndexLink from "./IndexLink";
 import IndexRoute from "./IndexRoute";
 import Link from "./Link";
@@ -25,6 +26,7 @@ export {
   RouterContext,
   VNode,
   createRoutes,
+  doAllAsyncBefore,
   match
 };
 
@@ -38,5 +40,6 @@ export default {
   Router,
   RouterContext,
   createRoutes,
+  doAllAsyncBefore,
   match
 };
diff --git a/packages/inferno-server/package.json b/packages/inferno-server/package.json
index 5edff0aa0..2e6478b41 100644
--- a/packages/inferno-server/package.json
+++ b/packages/inferno-server/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-server",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides methods to render HTML strings from Inferno elements",
   "author": {
@@ -30,14 +30,14 @@
   "typings": "dist/index.d.ts",
   "repository": "https://github.com/infernojs/inferno",
   "dependencies": {
-    "inferno": "3.7.0",
-    "inferno-shared": "3.7.0",
-    "inferno-vnode-flags": "3.7.0"
+    "inferno": "3.7.1",
+    "inferno-shared": "3.7.1",
+    "inferno-vnode-flags": "3.7.1"
   },
   "devDependencies": {
-    "inferno-component": "3.7.0",
-    "inferno-create-class": "3.7.0",
-    "inferno-create-element": "3.7.0"
+    "inferno-component": "3.7.1",
+    "inferno-create-class": "3.7.1",
+    "inferno-create-element": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno-shared/package.json b/packages/inferno-shared/package.json
index a47ef553e..b3af4a3b1 100644
--- a/packages/inferno-shared/package.json
+++ b/packages/inferno-shared/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-shared",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Helpers functions for Inferno",
   "author": {
diff --git a/packages/inferno-utils/package.json b/packages/inferno-utils/package.json
index cd7a134ba..09a4a2c5a 100644
--- a/packages/inferno-utils/package.json
+++ b/packages/inferno-utils/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-utils",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "description": "Meta test utilities which are not for userland",
   "main": "index.js",
   "scripts": {
diff --git a/packages/inferno-vnode-flags/package.json b/packages/inferno-vnode-flags/package.json
index a059de3e5..42440df9f 100644
--- a/packages/inferno-vnode-flags/package.json
+++ b/packages/inferno-vnode-flags/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-vnode-flags",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides an enum of all possible VNode Flags used when calling Inferno.createVNode",
   "author": {
diff --git a/packages/inferno/package.json b/packages/inferno/package.json
index 73e45564e..c28b13813 100644
--- a/packages/inferno/package.json
+++ b/packages/inferno/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "An extremely fast, React-like JavaScript library for building modern user interfaces",
   "author": {
@@ -37,7 +37,7 @@
   "typings": "dist/index.d.ts",
   "repository": "https://github.com/infernojs/inferno",
   "dependencies": {
-    "inferno-shared": "3.7.0",
-    "inferno-vnode-flags": "3.7.0"
+    "inferno-shared": "3.7.1",
+    "inferno-vnode-flags": "3.7.1"
   }
 }
diff --git a/packages/inferno/src/DOM/constants.ts b/packages/inferno/src/DOM/constants.ts
index 7ecb2eb2b..b4734d49c 100644
--- a/packages/inferno/src/DOM/constants.ts
+++ b/packages/inferno/src/DOM/constants.ts
@@ -30,6 +30,7 @@ booleanProps.add("novalidate");
 booleanProps.add("hidden");
 booleanProps.add("autoFocus");
 booleanProps.add("selected");
+booleanProps.add("indeterminate");
 
 export const namespaces = new Map<string, string>();
 namespaces.set("xlink:href", xlinkNS);
diff --git a/packages/inferno/src/DOM/events/delegation.ts b/packages/inferno/src/DOM/events/delegation.ts
index bef3deacd..8683c5ab0 100644
--- a/packages/inferno/src/DOM/events/delegation.ts
+++ b/packages/inferno/src/DOM/events/delegation.ts
@@ -50,7 +50,7 @@ export function handleEvent(name, lastEvent, nextEvent, dom) {
   }
 }
 
-function dispatchEvent(
+function dispatchEvents(
   event,
   target,
   items,
@@ -58,35 +58,31 @@ function dispatchEvent(
   isClick: boolean,
   eventData: IEventData
 ) {
-  const eventsToTrigger = items.get(target);
-
-  if (eventsToTrigger) {
-    count--;
-    // linkEvent object
-    eventData.dom = target;
-    if (eventsToTrigger.event) {
-      eventsToTrigger.event(eventsToTrigger.data, event);
-    } else {
-      eventsToTrigger(event);
-    }
-    if (event.cancelBubble) {
-      return;
+  let dom = target;
+  while (count > 0) {
+    const eventsToTrigger = items.get(dom);
+
+    if (eventsToTrigger) {
+      count--;
+      // linkEvent object
+      eventData.dom = dom;
+      if (eventsToTrigger.event) {
+        eventsToTrigger.event(eventsToTrigger.data, event);
+      } else {
+        eventsToTrigger(event);
+      }
+      if (event.cancelBubble) {
+        return;
+      }
     }
-  }
-  if (count > 0) {
-    const parentDom = target.parentNode;
+    dom = dom.parentNode;
 
     // Html Nodes can be nested fe: span inside button in that scenario browser does not handle disabled attribute on parent,
     // because the event listener is on document.body
     // Don't process clicks on disabled elements
-    if (
-      parentDom === null ||
-      (isClick && parentDom.nodeType === 1 && parentDom.disabled)
-    ) {
+    if (dom === null || (isClick && dom.disabled)) {
       return;
     }
-
-    dispatchEvent(event, parentDom, items, count, isClick, eventData);
   }
 }
 
@@ -121,7 +117,7 @@ function attachEventToDocument(name, delegatedRoots: IDelegate) {
         /* safari7 and phantomJS will crash */
       }
 
-      dispatchEvent(
+      dispatchEvents(
         event,
         event.target,
         delegatedRoots.items,
diff --git a/packages/inferno/src/DOM/hydration.ts b/packages/inferno/src/DOM/hydration.ts
index dec083a72..f0010008e 100644
--- a/packages/inferno/src/DOM/hydration.ts
+++ b/packages/inferno/src/DOM/hydration.ts
@@ -31,6 +31,7 @@ import {
   createClassComponentInstance,
   createFunctionalComponentInput,
   EMPTY_OBJ,
+  isSamePropsInnerHTML,
   replaceChild
 } from "./utils";
 import {
@@ -131,7 +132,7 @@ function hydrateElement(
   vNode.dom = dom;
   if (!isInvalid(children)) {
     hydrateChildren(children, dom, lifecycle, context, isSVG);
-  } else if (dom.firstChild !== null) {
+  } else if (dom.firstChild !== null && !isSamePropsInnerHTML(dom, props)) {
     dom.textContent = ""; // dom has content, but VNode has no children remove everything from DOM
   }
   if (props) {
diff --git a/packages/inferno/src/DOM/patching.ts b/packages/inferno/src/DOM/patching.ts
index 8aae69994..1eb173470 100644
--- a/packages/inferno/src/DOM/patching.ts
+++ b/packages/inferno/src/DOM/patching.ts
@@ -52,6 +52,7 @@ import {
   EMPTY_OBJ,
   insertOrAppend,
   isKeyed,
+  isSameInnerHTML,
   removeAllChildren,
   replaceChild,
   replaceLastChildAndUnmount,
@@ -443,18 +444,8 @@ export function patchComponent(
           ? combineFrom(nextState, null)
           : nextState;
         const lastProps = instance.props;
-        let childContext;
-        if (!isNullOrUndef(instance.getChildContext)) {
-          childContext = instance.getChildContext();
-        }
-
         nextVNode.children = instance;
         instance._isSVG = isSVG;
-        if (isNullOrUndef(childContext)) {
-          childContext = context;
-        } else {
-          childContext = combineFrom(context, childContext);
-        }
         const lastInput = instance._lastInput;
         let nextInput = instance._updateComponent(
           lastState,
@@ -466,6 +457,16 @@ export function patchComponent(
           false
         );
         let didUpdate = true;
+        // Update component before getting child context
+        let childContext;
+        if (!isNullOrUndef(instance.getChildContext)) {
+          childContext = instance.getChildContext();
+        }
+        if (isNullOrUndef(childContext)) {
+          childContext = context;
+        } else {
+          childContext = combineFrom(context, childContext);
+        }
 
         instance._childContext = childContext;
         if (isInvalid(nextInput)) {
@@ -1034,7 +1035,7 @@ export function patchProp(
       const nextHtml = nextValue && nextValue.__html;
 
       if (lastHtml !== nextHtml) {
-        if (!isNullOrUndef(nextHtml)) {
+        if (!isNullOrUndef(nextHtml) && !isSameInnerHTML(dom, nextHtml)) {
           dom.innerHTML = nextHtml;
         }
       }
diff --git a/packages/inferno/src/DOM/unmounting.ts b/packages/inferno/src/DOM/unmounting.ts
index d4aeb233a..83a306ca5 100644
--- a/packages/inferno/src/DOM/unmounting.ts
+++ b/packages/inferno/src/DOM/unmounting.ts
@@ -95,11 +95,6 @@ export function unmountComponent(
     }
   }
   if (parentDom) {
-    let lastInput = instance._lastInput;
-
-    if (isNullOrUndef(lastInput)) {
-      lastInput = instance;
-    }
     removeChild(parentDom, dom);
   }
   if (
diff --git a/packages/inferno/src/DOM/utils.ts b/packages/inferno/src/DOM/utils.ts
index 3f8e13901..d7c3c015d 100644
--- a/packages/inferno/src/DOM/utils.ts
+++ b/packages/inferno/src/DOM/utils.ts
@@ -270,3 +270,22 @@ export function isKeyed(lastChildren: VNode[], nextChildren: VNode[]): boolean {
     !isNullOrUndef(lastChildren[0].key)
   );
 }
+
+export function isSameInnerHTML(dom: Element, innerHTML: string): boolean {
+  const tempdom = document.createElement("i");
+
+  tempdom.innerHTML = innerHTML;
+  return tempdom.innerHTML === dom.innerHTML;
+}
+
+export function isSamePropsInnerHTML(
+  dom: Element,
+  props: Props | null
+): boolean {
+  return Boolean(
+    props &&
+      props.dangerouslySetInnerHTML &&
+      props.dangerouslySetInnerHTML.__html &&
+      isSameInnerHTML(dom, props.dangerouslySetInnerHTML.__html)
+  );
+}


Test patch:
diff --git a/packages/inferno-component/__tests__/setState.spec.jsx b/packages/inferno-component/__tests__/setState.spec.jsx
index 2b101c3bd..3efddd143 100644
--- a/packages/inferno-component/__tests__/setState.spec.jsx
+++ b/packages/inferno-component/__tests__/setState.spec.jsx
@@ -750,4 +750,88 @@ describe("setState", () => {
 
     render(<Com />, container);
   });
+
+  it("Should keep context in sync with state #1182", () => {
+    function Child(props, context) {
+      return (
+        <div>
+          {(context.active ? "ACTIVE" : "INACTIVE") +
+            "   :   " +
+            (context.state.active ? "ACTIVE" : "INACTIVE")}
+        </div>
+      );
+    }
+
+    class Container extends Component {
+      constructor(props, context) {
+        super(props, context);
+
+        this.state = {
+          active: false
+        };
+      }
+
+      getChildContext() {
+        return {
+          active: this.state.active,
+          state: this.state
+        };
+      }
+
+      componentWillReceiveProps(nextProps) {
+        if (this.state.active !== nextProps.active) {
+          this.setState({
+            active: nextProps.active
+          });
+        }
+      }
+
+      render() {
+        return <Child />;
+      }
+    }
+
+    let updater;
+    class App extends Component {
+      constructor(props, context) {
+        super(props, context);
+
+        this.state = {
+          active: false
+        };
+
+        updater = this.didClick.bind(this);
+      }
+
+      didClick(e) {
+        this.setState({
+          active: !this.state.active
+        });
+      }
+
+      render() {
+        return (
+          <div>
+            <Container active={this.state.active} />
+          </div>
+        );
+      }
+    }
+
+    render(<App />, container);
+
+    expect(container.textContent).toBe("INACTIVE   :   INACTIVE");
+
+    updater();
+
+    expect(container.textContent).toBe("ACTIVE   :   ACTIVE");
+
+    updater();
+
+    expect(container.textContent).toBe("INACTIVE   :   INACTIVE");
+
+    updater();
+
+    expect(container.textContent).toBe("ACTIVE   :   ACTIVE");
+  });
 });
diff --git a/packages/inferno-create-element/__tests__/formelements.spec.jsx b/packages/inferno-create-element/__tests__/formelements.spec.jsx
index 289490bf5..1f58e13ea 100644
--- a/packages/inferno-create-element/__tests__/formelements.spec.jsx
+++ b/packages/inferno-create-element/__tests__/formelements.spec.jsx
@@ -416,6 +416,49 @@ describe("FormElements", () => {
       input = container.querySelector("input");
       expect(input.checked).toBe(true);
     });
+
+    it("Should support indeterminate state", () => {
+      let input;
+
+      render(
+        <input
+          ref={dom => (input = dom)}
+          type="checkbox"
+          checked={false}
+          indeterminate={true}
+        />,
+        container
+      );
+
+      expect(input.indeterminate).toBe(true);
+      expect(input.checked).toBe(false);
+
+      render(
+        <input
+          ref={dom => (input = dom)}
+          type="checkbox"
+          checked={false}
+          indeterminate={false}
+        />,
+        container
+      );
+
+      expect(input.indeterminate).toBe(false);
+      expect(input.checked).toBe(false);
+
+      render(
+        <input
+          ref={dom => (input = dom)}
+          type="checkbox"
+          checked={true}
+          indeterminate={false}
+        />,
+        container
+      );
+
+      expect(input.indeterminate).toBe(false);
+      expect(input.checked).toBe(true);
+    });
   });
 
   // https://facebook.github.io/react/docs/forms.html
diff --git a/packages/inferno-router/__tests__/asyncBeforeBrowser.spec.jsx b/packages/inferno-router/__tests__/asyncBeforeBrowser.spec.jsx
new file mode 100644
index 000000000..4f98fcff2
--- /dev/null
+++ b/packages/inferno-router/__tests__/asyncBeforeBrowser.spec.jsx
@@ -0,0 +1,267 @@
+import { createMemoryHistory } from "history";
+import { render } from "inferno";
+import Component from "inferno-component";
+import { innerHTML } from "inferno-utils";
+import {
+  IndexRoute,
+  Link,
+  Route,
+  Router,
+  match,
+  doAllAsyncBefore
+} from "inferno-router";
+
+const browserHistory = createMemoryHistory();
+
+var dataStore = {};
+
+class PageOne extends Component {
+  static fetchData(params) {
+    return new Promise(function(resolve, reject) {
+      setTimeout(() => {
+        dataStore["pageOne"] = "data page one";
+        resolve();
+      }, 0);
+    });
+  }
+
+  render() {
+    return <div>Page One <span>{dataStore.pageOne}</span></div>;
+  }
+}
+
+class PageTwo extends Component {
+  static fetchData(params) {
+    // console.log('Fetch Data PageTwo');
+    return new Promise(function(resolve, reject) {
+      setTimeout(() => {
+        dataStore["pageTwo"] = "data page two";
+        resolve();
+      }, 0);
+    });
+  }
+
+  render() {
+    return (
+      <div>Page Two <span>{dataStore.pageTwo}</span>{this.props.children}</div>
+    );
+  }
+}
+
+class PageNoData extends Component {
+  static fetchData(params) {
+    return new Promise(function(resolve, reject) {
+      setTimeout(() => {
+        resolve();
+      }, 0);
+    });
+  }
+
+  render() {
+    return <div>Page No Data</div>;
+  }
+}
+
+class PageNoAsync extends Component {
+  render() {
+    return <div>Page No Async</div>;
+  }
+}
+
+class Section extends Component {
+  static fetchData(params) {
+    return new Promise(function(resolve, reject) {
+      setTimeout(() => {
+        dataStore["section"] = "data section";
+        resolve();
+      }, 0);
+    });
+  }
+
+  render() {
+    return <div>Section <span>{dataStore.section}</span></div>;
+  }
+}
+
+describe("Router (jsx) asyncBefore", () => {
+  describe("with browser rendering", () => {
+    let container;
+
+    beforeEach(function() {
+      dataStore = {};
+      browserHistory.push("/");
+      container = document.createElement("div");
+      document.body.appendChild(container);
+    });
+
+    afterEach(function() {
+      render(null, container);
+      document.body.removeChild(container);
+    });
+
+    it("should resolve single asyncBefore", done => {
+      function didEnter() {
+        requestAnimationFrame(() => {
+          expect(innerHTML(container.innerHTML)).toBe(
+            innerHTML("<div>Page One <span>data page one</span></div>")
+          );
+          done();
+        });
+      }
+
+      const routes = (
+        <Router history={browserHistory}>
+          <Route
+            key="test"
+            path={"/"}
+            asyncBefore={PageOne.fetchData}
+            onEnter={didEnter}
+            component={PageOne}
+          />
+        </Router>
+      );
+
+      // Need to prime the route props with data on first render. With SSR this should be done by hydration
+      // so makes sense not to perform automagically in router.
+      const renderProps = match(routes, "/");
+      doAllAsyncBefore(renderProps).then(() => {
+        // Then render
+        render(routes, container);
+      });
+    });
+
+    it("should resolve nested asyncBefore", done => {
+      function didEnter() {
+        requestAnimationFrame(() => {
+          expect(innerHTML(container.innerHTML)).toBe(
+            innerHTML(
+              "<div>Page Two <span>data page two</span><div>Section <span>data section</span></div></div>"
+            )
+          );
+          done();
+        });
+      }
+
+      const routes = (
+        <Router history={browserHistory}>
+          <Route path="/" asyncBefore={PageTwo.fetchData} component={PageTwo}>
+            <Route
+              asyncBefore={Section.fetchData}
+              onEnter={didEnter}
+              component={Section}
+            />
+          </Route>
+        </Router>
+      );
+
+      const renderProps = match(routes, "/");
+      doAllAsyncBefore(renderProps).then(() => {
+        // Then render
+        render(routes, container);
+      });
+    });
+
+    it("should render without asyncBefore provided", done => {
+      function didEnter() {
+        requestAnimationFrame(() => {
+          expect(innerHTML(container.innerHTML)).toBe(
+            innerHTML("<div>Page No Async</div>")
+          );
+          done();
+        });
+      }
+
+      const routes = (
+        <Router history={browserHistory}>
+          <Route path="/" component={PageNoAsync} onEnter={didEnter} />
+        </Router>
+      );
+
+      const renderProps = match(routes, "/");
+      doAllAsyncBefore(renderProps).then(() => {
+        // Then render
+        render(routes, container);
+      });
+    });
+
+    it("should handle navigation", done => {
+      class TransientPageOne extends Component {
+        static fetchData(params) {
+          return new Promise(function(resolve, reject) {
+            setTimeout(() => {
+              dataStore["pageOne"] = "data page one";
+              resolve();
+            }, 0);
+          });
+        }
+
+        componentDidMount() {
+          requestAnimationFrame(() => {
+            requestAnimationFrame(() => {
+              // console.log('push')
+              this.context.router.push("/test3");
+            });
+          });
+        }
+
+        render() {
+          return <div>Page One <span>{dataStore.pageOne}</span></div>;
+        }
+      }
+
+      function didEnterOne() {
+        // console.log('Step 1.1')
+        requestAnimationFrame(() => {
+          // console.log('Step 1.2')
+          expect(innerHTML(container.innerHTML)).toBe(
+            innerHTML("<div>Page One <span>data page one</span></div>")
+          );
+          // console.log('Step 1.3')
+        });
+      }
+
+      function didEnterTwo() {
+        // console.log('Step 2.1')
+        requestAnimationFrame(() => {
+          // console.log('Step 2.2')
+          expect(innerHTML(container.innerHTML)).toBe(
+            innerHTML("<div>Page Two <span>data page two</span></div>")
+          );
+          // console.log('Step 2.3')
+          done();
+        });
+      }
+
+      const routes = (
+        <Router
+          history={browserHistory}
+          asyncBefore={url => {
+            // console.log('Async before...')
+            return doAllAsyncBefore(match(routes, url));
+          }}
+        >
+          <Route
+            path="/"
+            asyncBefore={TransientPageOne.fetchData}
+            onEnter={didEnterOne}
+            component={TransientPageOne}
+          />
+          <Route
+            path="/test3"
+            asyncBefore={PageTwo.fetchData}
+            onEnter={didEnterTwo}
+            component={PageTwo}
+          />
+        </Router>
+      );
+
+      // Need to prime the route props with data on first render. With SSR this should be done by hydration
+      // so makes sense not to perform automagically in router.
+      const renderProps = match(routes, "/");
+      doAllAsyncBefore(renderProps).then(() => {
+        // Then render
+        render(routes, container);
+      });
+    });
+  });
+});
diff --git a/packages/inferno-router/__tests__/asyncBeforeServer.spec.jsx b/packages/inferno-router/__tests__/asyncBeforeServer.spec.jsx
new file mode 100644
index 000000000..283404acf
--- /dev/null
+++ b/packages/inferno-router/__tests__/asyncBeforeServer.spec.jsx
@@ -0,0 +1,176 @@
+import { createMemoryHistory } from "history";
+import { render } from "inferno";
+import Component from "inferno-component";
+import InfernoServer from "inferno-server";
+import createElement from "inferno-create-element";
+import { innerHTML } from "inferno-utils";
+import {
+  IndexRoute,
+  Link,
+  Route,
+  Router,
+  RouterContext,
+  match,
+  doAllAsyncBefore
+} from "inferno-router";
+
+const browserHistory = createMemoryHistory();
+
+var dataStore = {};
+
+class PageOne extends Component {
+  static fetchData(params) {
+    return new Promise(function(resolve, reject) {
+      setTimeout(() => {
+        dataStore["pageOne"] = "data page one";
+        resolve();
+      }, 0);
+    });
+  }
+
+  render() {
+    return <div>Page One <span>{dataStore.pageOne}</span></div>;
+  }
+}
+
+class PageTwo extends Component {
+  static fetchData(params) {
+    return new Promise(function(resolve, reject) {
+      setTimeout(() => {
+        dataStore["pageTwo"] = "data page two";
+        resolve();
+      }, 0);
+    });
+  }
+
+  render() {
+    return (
+      <div>Page Two <span>{dataStore.pageTwo}</span>{this.props.children}</div>
+    );
+  }
+}
+
+class PageNoData extends Component {
+  static fetchData(params) {
+    return new Promise(function(resolve, reject) {
+      setTimeout(() => {
+        resolve();
+      }, 0);
+    });
+  }
+
+  render() {
+    return <div>Page No Data</div>;
+  }
+}
+
+class PageNoAsync extends Component {
+  render() {
+    return <div>Page No Async</div>;
+  }
+}
+
+class Section extends Component {
+  static fetchData(params) {
+    return new Promise(function(resolve, reject) {
+      setTimeout(() => {
+        dataStore["section"] = "data section";
+        resolve();
+      }, 0);
+    });
+  }
+
+  render() {
+    return <div>Section <span>{dataStore.section}</span></div>;
+  }
+}
+
+describe("Router (jsx) asyncBefore", () => {
+  beforeEach(function() {
+    dataStore = {};
+  });
+
+  describe("with	SSR", () => {
+    it("should resolve single asyncBefore", done => {
+      const routes = (
+        <Router history={browserHistory}>
+          <Route path="/" asyncBefore={PageOne.fetchData} component={PageOne} />
+        </Router>
+      );
+
+      const renderProps = match(routes, "/");
+      doAllAsyncBefore(renderProps).then(() => {
+        const html = InfernoServer.renderToString(
+          createElement(RouterContext, renderProps)
+        );
+        expect(html).toBe(
+          innerHTML("<div>Page One <span>data page one</span></div>")
+        );
+        done();
+      });
+    });
+
+    it("should resolve nested asyncBefore", done => {
+      const routes = (
+        <Router history={browserHistory}>
+          <Route path="/" asyncBefore={PageTwo.fetchData} component={PageTwo}>
+            <Route asyncBefore={Section.fetchData} component={Section} />
+          </Route>
+        </Router>
+      );
+
+      const renderProps = match(routes, "/");
+      doAllAsyncBefore(renderProps).then(() => {
+        const html = InfernoServer.renderToString(
+          createElement(RouterContext, renderProps)
+        );
+        expect(html).toBe(
+          innerHTML(
+            "<div>Page Two <span>data page two</span><div>Section <span>data section</span></div></div>"
+          )
+        );
+        done();
+      });
+    });
+
+    it("should resolve multiple nested asyncBefore", done => {
+      const routes = (
+        <Router history={browserHistory}>
+          <Route path="/" asyncBefore={PageTwo.fetchData} component={PageTwo}>
+            <Route asyncBefore={Section.fetchData} component={Section} />
+          </Route>
+        </Router>
+      );
+
+      const renderProps = match(routes, "/");
+      doAllAsyncBefore(renderProps).then(() => {
+        const html = InfernoServer.renderToString(
+          createElement(RouterContext, renderProps)
+        );
+        expect(html).toBe(
+          innerHTML(
+            "<div>Page Two <span>data page two</span><div>Section <span>data section</span></div></div>"
+          )
+        );
+        done();
+      });
+    });
+
+    it("should render without asyncBefore provided", done => {
+      const routes = (
+        <Router history={browserHistory}>
+          <Route path="/" component={PageNoAsync} />
+        </Router>
+      );
+
+      const renderProps = match(routes, "/");
+      doAllAsyncBefore(renderProps).then(() => {
+        const html = InfernoServer.renderToString(
+          createElement(RouterContext, renderProps)
+        );
+        expect(html).toBe(innerHTML("<div>Page No Async</div>"));
+        done();
+      });
+    });
+  });
+});
diff --git a/packages/inferno-test-utils/package.json b/packages/inferno-test-utils/package.json
index 922fd18be..97b5da2c7 100644
--- a/packages/inferno-test-utils/package.json
+++ b/packages/inferno-test-utils/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-test-utils",
-  "version": "3.7.0",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Suite of utilities for testing Inferno applications",
   "author": {
@@ -21,15 +21,15 @@
     "utils"
   ],
   "dependencies": {
-    "inferno": "3.7.0",
-    "inferno-create-class": "3.7.0",
-    "inferno-create-element": "3.7.0",
-    "inferno-shared": "3.7.0",
-    "inferno-vnode-flags": "3.7.0"
+    "inferno": "3.7.1",
+    "inferno-create-class": "3.7.1",
+    "inferno-create-element": "3.7.1",
+    "inferno-shared": "3.7.1",
+    "inferno-vnode-flags": "3.7.1"
   },
   "devDependencies": {
-    "inferno-component": "3.7.0",
-    "inferno-server": "3.7.0"
+    "inferno-component": "3.7.1",
+    "inferno-server": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno/__tests__/patching.spec.js b/packages/inferno/__tests__/patching.spec.js
index d5b693bfc..6ffcc44df 100644
--- a/packages/inferno/__tests__/patching.spec.js
+++ b/packages/inferno/__tests__/patching.spec.js
@@ -103,4 +103,18 @@ describe("patching routine", () => {
     render(createTextVNode("a"), container);
     expect(container.innerHTML).toEqual("a");
   });
+
+  it("Should not patch same innerHTML", () => {
+    container.innerHTML = "<span><span><span>child</span></span</span>";
+
+    const childelem = container.firstElementChild.firstElementChild;
+    const props = { dangerouslySetInnerHTML: { __html: "<span>child</span>" } };
+
+    const bar = createVNode(2, "span", null, null, props, null, null, true);
+    const foo = createVNode(2, "span", null, [bar], null, null, null, true);
+
+    render(foo, container);
+
+    expect(childelem).toBe(container.firstElementChild.firstElementChild);
+  });
 });