Repository: infernojs/inferno
Pull request #1161 (2017-07-04 15:05:42)

Allow async data fetching prior to performing transition
**Objective**

Add support for async data fetching prior to performing transitions in router. The purpose of asyncBefore is to maintain standard browser behaviour when navigating between pages. 

Unlike getComponent this allows all the matched fetchers to resolve concurrently. The attribute name (asyncBefore) also makes the router declarations more readable.

I have added tests and updated the docs.

**Closes Issue**

It closes Issue #1045 


coveralls: 
[![Coverage Status](https://coveralls.io/builds/12247345/badge)](https://coveralls.io/builds/12247345)

Coverage increased (+0.09%) to 88.273% when pulling **cd40b6ca0d4879221f642c43145dfa9b8365415b on jhsware:feature/async-before** into **ca16bf36f45729811662b583e0dedc9c7268b3d3 on infernojs:dev**.

LukeSheard: I definitely thought I'd already submitted a PR for this... In summary I think it was:
- Please split this into two commits, a prettier commit and the actual change. I can't work out what's actually going on. 
- I'm really not too happy with the `asyncBefore` prop name. But I can't remember my suggestion off the top of my head. 
Havunen: btw I ran prettier for whole solution againts dev so after rebasing this PR it should show changes only
Havunen: @jhsware can you rebase this PR pls?
jhsware: @Havunen I'll rebase it! @LukeSheard if you have a better prop name let me know
coveralls: 
[![Coverage Status](https://coveralls.io/builds/12355111/badge)](https://coveralls.io/builds/12355111)

Coverage increased (+0.09%) to 88.191% when pulling **342ba372b1b39ca2841106dda19e27a591007077 on jhsware:feature/async-before** into **7b3e4e3415df54db7f7e9829aa6085524cc2a55a on infernojs:dev**.

jhsware: @LukeSheard now the PR is readable, sorry about the state of the first submission. I was caught out by the prettier

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 c0023a9da..88d46068a 100644
--- a/lerna.json
+++ b/lerna.json
@@ -8,5 +8,5 @@
   "packages": [
     "packages/*"
   ],
-  "version": "3.6.4"
+  "version": "3.7.1"
 }
diff --git a/package.json b/package.json
index 3d4df0bee..b490d0a4d 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "name": "inferno-build",
   "private": true,
   "license": "MIT",
-  "version": "3.6.4",
+  "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.2",
-    "@types/lodash": "^4.14.68",
-    "@types/node": "^8.0.10",
+    "@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,37 +93,37 @@
     "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.1",
+    "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",
     "pre-commit": "^1.2.2",
-    "prettier": "^1.5.2",
+    "prettier": "^1.5.3",
     "rimraf": "^2.6.1",
-    "rollup": "0.45.0",
+    "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.7",
+    "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.1",
+    "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 f8fb9f027..e2214d20d 100644
--- a/packages/inferno-compat/package.json
+++ b/packages/inferno-compat/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-compat",
-  "version": "3.6.4",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides a compatibility with React codebases",
   "author": {
@@ -23,17 +23,17 @@
     "user interfaces"
   ],
   "dependencies": {
-    "inferno": "3.6.4",
-    "inferno-component": "3.6.4",
-    "inferno-create-class": "3.6.4",
-    "inferno-create-element": "3.6.4",
-    "inferno-shared": "3.6.4",
+    "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.6.4",
+    "inferno-vnode-flags": "3.7.1",
     "rc-css-transition-group-modern": "^2.1.6"
   },
   "devDependencies": {
-    "inferno-hyperscript": "3.6.4"
+    "inferno-hyperscript": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno-component/package.json b/packages/inferno-component/package.json
index 009d35a68..7089241b4 100644
--- a/packages/inferno-component/package.json
+++ b/packages/inferno-component/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-component",
-  "version": "3.6.4",
+  "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.6.4",
-    "inferno-shared": "3.6.4",
-    "inferno-vnode-flags": "3.6.4"
+    "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 16e6c041f..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.6.4",
+  "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.6.4",
-    "inferno-shared": "3.6.4"
+    "inferno-component": "3.7.1",
+    "inferno-shared": "3.7.1"
   },
   "devDependencies": {
-    "inferno": "3.6.4",
-    "inferno-create-element": "3.6.4"
+    "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 9af6f621a..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.6.4",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides methods to create Inferno VNodes",
   "author": {
@@ -23,10 +23,10 @@
     "user interfaces"
   ],
   "dependencies": {
-    "inferno": "3.6.4",
-    "inferno-component": "3.6.4",
-    "inferno-shared": "3.6.4",
-    "inferno-vnode-flags": "3.6.4"
+    "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 22fbfcbe4..5d348859e 100644
--- a/packages/inferno-devtools/package.json
+++ b/packages/inferno-devtools/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-devtools",
-  "version": "3.6.4",
+  "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.6.4",
-    "inferno-component": "3.6.4",
-    "inferno-shared": "3.6.4",
-    "inferno-vnode-flags": "3.6.4"
+    "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 269ffc130..acd0424e5 100644
--- a/packages/inferno-hyperscript/package.json
+++ b/packages/inferno-hyperscript/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-hyperscript",
-  "version": "3.6.4",
+  "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.6.4",
-    "inferno-shared": "3.6.4",
-    "inferno-vnode-flags": "3.6.4"
+    "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 2e7106397..36eafa468 100644
--- a/packages/inferno-mobx/package.json
+++ b/packages/inferno-mobx/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-mobx",
-  "version": "3.6.4",
+  "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.6.4",
-    "inferno-component": "3.6.4",
-    "inferno-create-class": "3.6.4",
-    "inferno-create-element": "3.6.4",
-    "inferno-shared": "3.6.4",
+    "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 c4e7b1f5e..149b82595 100644
--- a/packages/inferno-redux/package.json
+++ b/packages/inferno-redux/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-redux",
-  "version": "3.6.4",
+  "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.6.4",
-    "inferno-component": "3.6.4",
-    "inferno-create-element": "3.6.4",
-    "inferno-shared": "3.6.4",
+    "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.6.4"
+    "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 f87bb0f1c..0da4500db 100644
--- a/packages/inferno-router/package.json
+++ b/packages/inferno-router/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-router",
-  "version": "3.6.4",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Provides routing functionality for Inferno",
   "author": {
@@ -25,12 +25,12 @@
   ],
   "repository": "https://github.com/infernojs/inferno",
   "dependencies": {
-    "history": "^4.6.1",
-    "inferno": "3.6.4",
-    "inferno-component": "3.6.4",
-    "inferno-create-element": "3.6.4",
-    "inferno-shared": "3.6.4",
-    "inferno-vnode-flags": "3.6.4",
+    "history": "^4.6.3",
+    "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 5b1a74e9f..2e6478b41 100644
--- a/packages/inferno-server/package.json
+++ b/packages/inferno-server/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-server",
-  "version": "3.6.4",
+  "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.6.4",
-    "inferno-shared": "3.6.4",
-    "inferno-vnode-flags": "3.6.4"
+    "inferno": "3.7.1",
+    "inferno-shared": "3.7.1",
+    "inferno-vnode-flags": "3.7.1"
   },
   "devDependencies": {
-    "inferno-component": "3.6.4",
-    "inferno-create-class": "3.6.4",
-    "inferno-create-element": "3.6.4"
+    "inferno-component": "3.7.1",
+    "inferno-create-class": "3.7.1",
+    "inferno-create-element": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [
diff --git a/packages/inferno-server/src/renderToString.ts b/packages/inferno-server/src/renderToString.ts
index ee9fb53b5..c41206a8a 100644
--- a/packages/inferno-server/src/renderToString.ts
+++ b/packages/inferno-server/src/renderToString.ts
@@ -98,11 +98,6 @@ function renderVNodeToString(
           if (!props.checked) {
             renderedString += ` checked="${value}"`;
           }
-        } else if (type === "option" && prop === "value") {
-          // Parent value sets children value
-          if (value === parent.props.value) {
-            renderedString += ` selected`;
-          }
         } else {
           if (isString(value)) {
             renderedString += ` ${prop}="${escapeText(value)}"`;
@@ -113,6 +108,10 @@ function renderVNodeToString(
           }
         }
       }
+      if (type === "option" && typeof props.value !== "undefined" && props.value === parent.props.value) {
+        // Parent value sets children value
+        renderedString += ` selected`;
+      }
     }
     if (isVoidElement) {
       renderedString += `>`;
diff --git a/packages/inferno-shared/package.json b/packages/inferno-shared/package.json
index c9e7d805b..b3af4a3b1 100644
--- a/packages/inferno-shared/package.json
+++ b/packages/inferno-shared/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-shared",
-  "version": "3.6.4",
+  "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 6f6316ccf..09a4a2c5a 100644
--- a/packages/inferno-utils/package.json
+++ b/packages/inferno-utils/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno-utils",
-  "version": "3.6.4",
+  "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 951c7ac1d..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.6.4",
+  "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 aebb62a56..c28b13813 100644
--- a/packages/inferno/package.json
+++ b/packages/inferno/package.json
@@ -1,6 +1,6 @@
 {
   "name": "inferno",
-  "version": "3.6.4",
+  "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.6.4",
-    "inferno-vnode-flags": "3.6.4"
+    "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/patching.ts b/packages/inferno/src/DOM/patching.ts
index 786f17eb7..05467347e 100644
--- a/packages/inferno/src/DOM/patching.ts
+++ b/packages/inferno/src/DOM/patching.ts
@@ -443,18 +443,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 +456,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)) {
@@ -904,7 +904,7 @@ export function patchKeyedChildren(
             nextNode = nextPos < b.length ? b[nextPos].dom : null;
             insertOrAppend(
               dom,
-              mount(node, dom, lifecycle, context, isSVG),
+              mount(node, null, lifecycle, context, isSVG),
               nextNode
             );
           } else {
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 (


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-server/__tests__/creation-queuestream.spec.browser.js b/packages/inferno-server/__tests__/creation-queuestream.spec.browser.js
index 4b24e68ca..25528230c 100644
--- a/packages/inferno-server/__tests__/creation-queuestream.spec.browser.js
+++ b/packages/inferno-server/__tests__/creation-queuestream.spec.browser.js
@@ -83,6 +83,18 @@ describe("SSR Creation Queue Streams - (non-JSX)", () => {
         ),
       result: '<div><span style="border-left: 10px;"></span></div>'
     },
+    // TODO: Fix this
+    // {
+    //   description: "should render select element with selected property",
+    //   template: () =>
+    //     createElement('select', {
+    //       value: 'dog'
+    //     }, [
+    //       createElement('option', {value: 'cat'}, 'A cat'),
+    //       createElement('option', {value: 'dog'}, 'A dog')
+    //     ]),
+    //   result: '<select value="dog"><option value="cat">A cat</option><option value="dog" selected>A dog</option></select>'
+    // },
     {
       description: "should render div with span child and styling #2",
       template: () =>
diff --git a/packages/inferno-server/__tests__/creation-stream.spec.browser.js b/packages/inferno-server/__tests__/creation-stream.spec.browser.js
index c10c653cd..e352df9ec 100644
--- a/packages/inferno-server/__tests__/creation-stream.spec.browser.js
+++ b/packages/inferno-server/__tests__/creation-stream.spec.browser.js
@@ -30,6 +30,18 @@ describe("SSR Creation Streams - (non-JSX)", () => {
         ),
       result: '<div><span style="border-left: 10px;"></span></div>'
     },
+    // TODO: Fix this
+    // {
+    //   description: "should render select element with selected property",
+    //   template: () =>
+    //     createElement('select', {
+    //       value: 'dog'
+    //     }, [
+    //       createElement('option', {value: 'cat'}, 'A cat'),
+    //       createElement('option', {value: 'dog'}, 'A dog')
+    //     ]),
+    //   result: '<select value="dog"><option value="cat">A cat</option><option value="dog" selected>A dog</option></select>'
+    // },
     {
       description: "should render div with span child and styling #2",
       template: () =>
diff --git a/packages/inferno-server/__tests__/creation.spec.jsx b/packages/inferno-server/__tests__/creation.spec.jsx
index 8d8973a82..647de8198 100644
--- a/packages/inferno-server/__tests__/creation.spec.jsx
+++ b/packages/inferno-server/__tests__/creation.spec.jsx
@@ -98,7 +98,7 @@ describe("SSR Creation (JSX)", () => {
           <option value="dog">A dog</option>
         </select>,
       result:
-        '<select value="dog"><option>A cat</option><option selected>A dog</option></select>'
+        '<select value="dog"><option value="cat">A cat</option><option value="dog" selected>A dog</option></select>'
     },
     {
       description: "should render a text placeholder",
diff --git a/packages/inferno-test-utils/package.json b/packages/inferno-test-utils/package.json
index 13fdbc159..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.6.4",
+  "version": "3.7.1",
   "license": "MIT",
   "description": "Suite of utilities for testing Inferno applications",
   "author": {
@@ -21,15 +21,15 @@
     "utils"
   ],
   "dependencies": {
-    "inferno": "3.6.4",
-    "inferno-create-class": "3.6.4",
-    "inferno-create-element": "3.6.4",
-    "inferno-shared": "3.6.4",
-    "inferno-vnode-flags": "3.6.4"
+    "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.6.4",
-    "inferno-server": "3.6.4"
+    "inferno-component": "3.7.1",
+    "inferno-server": "3.7.1"
   },
   "rollup": {
     "bundledDependencies": [