From 2f8316a7c8964535e015c87a389a9ecba240be20 Mon Sep 17 00:00:00 2001 From: Jonathan Lehman Date: Wed, 22 Jul 2015 16:04:44 -0400 Subject: [PATCH] Reinitialize route recognizer handlers when props change - Ensure that Switcher is not rendering components with stale props --- dist/switcheroo.min.js | 2 +- modules/components/Switcher.js | 38 +++++++++++++++++++++++--------- test/components/Switcher_test.js | 2 -- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/dist/switcheroo.min.js b/dist/switcheroo.min.js index 75d5689..ed5f196 100644 --- a/dist/switcheroo.min.js +++ b/dist/switcheroo.min.js @@ -1 +1 @@ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("window"),require("react")):"function"==typeof define&&define.amd?define(["window","react"],e):"object"==typeof exports?exports.switcheroo=e(require("window"),require("react")):t.switcheroo=e(t.window,t.React)}(this,function(t,e){return function(t){function e(r){if(n[r])return n[r].exports;var a=n[r]={exports:{},id:r,loaded:!1};return t[r].call(a.exports,a,a.exports,e),a.loaded=!0,a.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";e.Switcher=n(1)},function(t,e,n){"use strict";var r=function(t){return t&&t.__esModule?t:{"default":t}},a=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},o=function(){function t(t,e){for(var n=0;na;a++)r+=t[a].path.length;e=e.substr(r);var i={path:e,handler:n};t.push(i)}function h(t,e,n,r){var a=e.routes;for(var o in a)if(a.hasOwnProperty(o)){var i=t.slice();s(i,o,a[o]),e.children[o]?h(i,e.children[o],n,r):n.call(r,i)}}function u(t){return"[object Array]"===Object.prototype.toString.call(t)}function c(t){this.string=t}function p(t){this.name=t}function l(t){this.name=t}function f(){}function d(t,e,n){"/"===t.charAt(0)&&(t=t.substr(1));for(var r=t.split("/"),a=[],o=0,i=r.length;i>o;o++){var s,h=r[o];(s=h.match(/^:([^\/]+)$/))?(a.push(new p(s[1])),e.push(s[1]),n.dynamics++):(s=h.match(/^\*([^\/]+)$/))?(a.push(new l(s[1])),e.push(s[1]),n.stars++):""===h?a.push(new f):(a.push(new c(h)),n.statics++)}return a}function y(t){this.charSpec=t,this.nextStates=[]}function g(t){return t.sort(function(t,e){if(t.types.stars!==e.types.stars)return t.types.stars-e.types.stars;if(t.types.stars){if(t.types.statics!==e.types.statics)return e.types.statics-t.types.statics;if(t.types.dynamics!==e.types.dynamics)return e.types.dynamics-t.types.dynamics}return t.types.dynamics!==e.types.dynamics?t.types.dynamics-e.types.dynamics:t.types.statics!==e.types.statics?e.types.statics-t.types.statics:0})}function v(t,e){for(var n=[],r=0,a=t.length;a>r;r++){var o=t[r];n=n.concat(o.match(e))}return n}function m(t){this.queryParams=t||{}}function w(t,e,n){for(var r=t.handlers,a=t.regex,o=e.match(a),i=1,s=new m(n),h=0,u=r.length;u>h;h++){for(var c=r[h],p=c.names,l={},f=0,d=p.length;d>f;f++)l[p[f]]=o[i++];s.push({handler:c.handler,params:l,isDynamic:!!p.length})}return s}function b(t,e){return e.eachChar(function(e){t=t.put(e)}),t}function C(t){return t=t.replace(/\+/gm,"%20"),decodeURIComponent(t)}a.prototype={to:function(t,e){var n=this.delegate;if(n&&n.willAddRoute&&(t=n.willAddRoute(this.matcher.target,t)),this.matcher.add(this.path,t),e){if(0===e.length)throw new Error("You must have an argument in the function passed to `to`");this.matcher.addChild(this.path,t,e,this.delegate)}return this}},o.prototype={add:function(t,e){this.routes[t]=e},addChild:function(t,e,n,r){var a=new o(e);this.children[t]=a;var s=i(t,a,r);r&&r.contextEntered&&r.contextEntered(e,s),n(s)}};var x=function(t,e){var n=new o;t(i("",n,this.delegate)),h([],n,function(t){e?e(this,t):this.add(t)},this)},S=["/",".","*","+","?","|","(",")","[","]","{","}","\\"],P=new RegExp("(\\"+S.join("|\\")+")","g");c.prototype={eachChar:function(t){for(var e,n=this.string,r=0,a=n.length;a>r;r++)e=n.charAt(r),t({validChars:e})},regex:function(){return this.string.replace(P,"\\$1")},generate:function(){return this.string}},p.prototype={eachChar:function(t){t({invalidChars:"/",repeat:!0})},regex:function(){return"([^/]+)"},generate:function(t){return t[this.name]}},l.prototype={eachChar:function(t){t({invalidChars:"",repeat:!0})},regex:function(){return"(.+)"},generate:function(t){return t[this.name]}},f.prototype={eachChar:function(){},regex:function(){return""},generate:function(){return""}},y.prototype={get:function(t){for(var e=this.nextStates,n=0,r=e.length;r>n;n++){var a=e[n],o=a.charSpec.validChars===t.validChars;if(o=o&&a.charSpec.invalidChars===t.invalidChars)return a}},put:function(t){var e;return(e=this.get(t))?e:(e=new y(t),this.nextStates.push(e),t.repeat&&e.nextStates.push(e),e)},match:function(t){for(var e,n,r,a=this.nextStates,o=[],i=0,s=a.length;s>i;i++)e=a[i],n=e.charSpec,"undefined"!=typeof(r=n.validChars)?-1!==r.indexOf(t)&&o.push(e):"undefined"!=typeof(r=n.invalidChars)&&-1===r.indexOf(t)&&o.push(e);return o}};var R=Object.create||function(t){function e(){}return e.prototype=t,new e};m.prototype=R({splice:Array.prototype.splice,slice:Array.prototype.slice,push:Array.prototype.push,length:0,queryParams:null});var E=function(){this.rootState=new y,this.names={}};E.prototype={add:function(t,e){for(var n,r=this.rootState,a="^",o={statics:0,dynamics:0,stars:0},i=[],s=[],h=!0,u=0,c=t.length;c>u;u++){var p=t[u],l=[],y=d(p.path,l,o);s=s.concat(y);for(var g=0,v=y.length;v>g;g++){var m=y[g];m instanceof f||(h=!1,r=r.put({validChars:"/"}),a+="/",r=b(r,m),a+=m.regex())}var w={handler:p.handler,names:l};i.push(w)}h&&(r=r.put({validChars:"/"}),a+="/"),r.handlers=i,r.regex=new RegExp(a+"$"),r.types=o,(n=e&&e.as)&&(this.names[n]={segments:s,handlers:i})},handlersFor:function(t){var e=this.names[t],n=[];if(!e)throw new Error("There is no route named "+t);for(var r=0,a=e.handlers.length;a>r;r++)n.push(e.handlers[r]);return n},hasRoute:function(t){return!!this.names[t]},generate:function(t,e){var n=this.names[t],r="";if(!n)throw new Error("There is no route named "+t);for(var a=n.segments,o=0,i=a.length;i>o;o++){var s=a[o];s instanceof f||(r+="/",r+=s.generate(e))}return"/"!==r.charAt(0)&&(r="/"+r),e&&e.queryParams&&(r+=this.generateQueryString(e.queryParams,n.handlers)),r},generateQueryString:function(t,e){var n=[],r=[];for(var a in t)t.hasOwnProperty(a)&&r.push(a);r.sort();for(var o=0,i=r.length;i>o;o++){a=r[o];var s=t[a];if(null!=s){var h=encodeURIComponent(a);if(u(s))for(var c=0,p=s.length;p>c;c++){var l=a+"[]="+encodeURIComponent(s[c]);n.push(l)}else h+="="+encodeURIComponent(s),n.push(h)}}return 0===n.length?"":"?"+n.join("&")},parseQueryString:function(t){for(var e=t.split("&"),n={},r=0;r2&&"[]"===i.slice(s-2)&&(h=!0,i=i.slice(0,s-2),n[i]||(n[i]=[])),a=o[1]?C(o[1]):""),h?n[i].push(a):n[i]=a}return n},recognize:function(t){var e,n,r,a,o=[this.rootState],i={},s=!1;if(a=t.indexOf("?"),-1!==a){var h=t.substr(a+1,t.length);t=t.substr(0,a),i=this.parseQueryString(h)}for(t=decodeURI(t),"/"!==t.charAt(0)&&(t="/"+t),e=t.length,e>1&&"/"===t.charAt(e-1)&&(t=t.substr(0,e-1),s=!0),n=0,r=t.length;r>n&&(o=v(o,t.charAt(n)),o.length);n++);var u=[];for(n=0,r=o.length;r>n;n++)o[n].handlers&&u.push(o[n]);o=g(u);var c=u[0];return c&&c.handlers?(s&&"(.+)$"===c.regex.source.slice(-5)&&(t+="/"),w(c,t,i)):void 0}},E.prototype.map=x,E.VERSION="0.1.5";var O=E;n(3).amd?(r=function(){return O}.call(e,n,e,t),!(void 0!==r&&(t.exports=r))):"undefined"!=typeof t&&t.exports?t.exports=O:"undefined"!=typeof this&&(this.RouteRecognizer=O)}).call(this)}).call(e,n(4)(t))},function(t,e,n){t.exports=function(){throw new Error("define cannot be used indirect")}},function(t,e,n){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},function(e,n,r){e.exports=t},function(t,n,r){t.exports=e}])}); \ No newline at end of file +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("window"),require("react")):"function"==typeof define&&define.amd?define(["window","react"],e):"object"==typeof exports?exports.switcheroo=e(require("window"),require("react")):t.switcheroo=e(t.window,t.React)}(this,function(t,e){return function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={exports:{},id:r,loaded:!1};return t[r].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";e.Switcher=n(1)},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var o=function(){function t(t,e){for(var n=0;ni;i++)r+=t[i].path.length;e=e.substr(r);var o={path:e,handler:n};t.push(o)}function h(t,e,n,r){var i=e.routes;for(var a in i)if(i.hasOwnProperty(a)){var o=t.slice();s(o,a,i[a]),e.children[a]?h(o,e.children[a],n,r):n.call(r,o)}}function u(t){return"[object Array]"===Object.prototype.toString.call(t)}function c(t){this.string=t}function p(t){this.name=t}function l(t){this.name=t}function f(){}function d(t,e,n){"/"===t.charAt(0)&&(t=t.substr(1));for(var r=t.split("/"),i=[],a=0,o=r.length;o>a;a++){var s,h=r[a];(s=h.match(/^:([^\/]+)$/))?(i.push(new p(s[1])),e.push(s[1]),n.dynamics++):(s=h.match(/^\*([^\/]+)$/))?(i.push(new l(s[1])),e.push(s[1]),n.stars++):""===h?i.push(new f):(i.push(new c(h)),n.statics++)}return i}function g(t){this.charSpec=t,this.nextStates=[]}function y(t){return t.sort(function(t,e){if(t.types.stars!==e.types.stars)return t.types.stars-e.types.stars;if(t.types.stars){if(t.types.statics!==e.types.statics)return e.types.statics-t.types.statics;if(t.types.dynamics!==e.types.dynamics)return e.types.dynamics-t.types.dynamics}return t.types.dynamics!==e.types.dynamics?t.types.dynamics-e.types.dynamics:t.types.statics!==e.types.statics?e.types.statics-t.types.statics:0})}function v(t,e){for(var n=[],r=0,i=t.length;i>r;r++){var a=t[r];n=n.concat(a.match(e))}return n}function m(t){this.queryParams=t||{}}function w(t,e,n){for(var r=t.handlers,i=t.regex,a=e.match(i),o=1,s=new m(n),h=0,u=r.length;u>h;h++){for(var c=r[h],p=c.names,l={},f=0,d=p.length;d>f;f++)l[p[f]]=a[o++];s.push({handler:c.handler,params:l,isDynamic:!!p.length})}return s}function b(t,e){return e.eachChar(function(e){t=t.put(e)}),t}function C(t){return t=t.replace(/\+/gm,"%20"),decodeURIComponent(t)}i.prototype={to:function(t,e){var n=this.delegate;if(n&&n.willAddRoute&&(t=n.willAddRoute(this.matcher.target,t)),this.matcher.add(this.path,t),e){if(0===e.length)throw new Error("You must have an argument in the function passed to `to`");this.matcher.addChild(this.path,t,e,this.delegate)}return this}},a.prototype={add:function(t,e){this.routes[t]=e},addChild:function(t,e,n,r){var i=new a(e);this.children[t]=i;var s=o(t,i,r);r&&r.contextEntered&&r.contextEntered(e,s),n(s)}};var S=function(t,e){var n=new a;t(o("",n,this.delegate)),h([],n,function(t){e?e(this,t):this.add(t)},this)},x=["/",".","*","+","?","|","(",")","[","]","{","}","\\"],P=new RegExp("(\\"+x.join("|\\")+")","g");c.prototype={eachChar:function(t){for(var e,n=this.string,r=0,i=n.length;i>r;r++)e=n.charAt(r),t({validChars:e})},regex:function(){return this.string.replace(P,"\\$1")},generate:function(){return this.string}},p.prototype={eachChar:function(t){t({invalidChars:"/",repeat:!0})},regex:function(){return"([^/]+)"},generate:function(t){return t[this.name]}},l.prototype={eachChar:function(t){t({invalidChars:"",repeat:!0})},regex:function(){return"(.+)"},generate:function(t){return t[this.name]}},f.prototype={eachChar:function(){},regex:function(){return""},generate:function(){return""}},g.prototype={get:function(t){for(var e=this.nextStates,n=0,r=e.length;r>n;n++){var i=e[n],a=i.charSpec.validChars===t.validChars;if(a=a&&i.charSpec.invalidChars===t.invalidChars)return i}},put:function(t){var e;return(e=this.get(t))?e:(e=new g(t),this.nextStates.push(e),t.repeat&&e.nextStates.push(e),e)},match:function(t){for(var e,n,r,i=this.nextStates,a=[],o=0,s=i.length;s>o;o++)e=i[o],n=e.charSpec,"undefined"!=typeof(r=n.validChars)?-1!==r.indexOf(t)&&a.push(e):"undefined"!=typeof(r=n.invalidChars)&&-1===r.indexOf(t)&&a.push(e);return a}};var R=Object.create||function(t){function e(){}return e.prototype=t,new e};m.prototype=R({splice:Array.prototype.splice,slice:Array.prototype.slice,push:Array.prototype.push,length:0,queryParams:null});var E=function(){this.rootState=new g,this.names={}};E.prototype={add:function(t,e){for(var n,r=this.rootState,i="^",a={statics:0,dynamics:0,stars:0},o=[],s=[],h=!0,u=0,c=t.length;c>u;u++){var p=t[u],l=[],g=d(p.path,l,a);s=s.concat(g);for(var y=0,v=g.length;v>y;y++){var m=g[y];m instanceof f||(h=!1,r=r.put({validChars:"/"}),i+="/",r=b(r,m),i+=m.regex())}var w={handler:p.handler,names:l};o.push(w)}h&&(r=r.put({validChars:"/"}),i+="/"),r.handlers=o,r.regex=new RegExp(i+"$"),r.types=a,(n=e&&e.as)&&(this.names[n]={segments:s,handlers:o})},handlersFor:function(t){var e=this.names[t],n=[];if(!e)throw new Error("There is no route named "+t);for(var r=0,i=e.handlers.length;i>r;r++)n.push(e.handlers[r]);return n},hasRoute:function(t){return!!this.names[t]},generate:function(t,e){var n=this.names[t],r="";if(!n)throw new Error("There is no route named "+t);for(var i=n.segments,a=0,o=i.length;o>a;a++){var s=i[a];s instanceof f||(r+="/",r+=s.generate(e))}return"/"!==r.charAt(0)&&(r="/"+r),e&&e.queryParams&&(r+=this.generateQueryString(e.queryParams,n.handlers)),r},generateQueryString:function(t,e){var n=[],r=[];for(var i in t)t.hasOwnProperty(i)&&r.push(i);r.sort();for(var a=0,o=r.length;o>a;a++){i=r[a];var s=t[i];if(null!=s){var h=encodeURIComponent(i);if(u(s))for(var c=0,p=s.length;p>c;c++){var l=i+"[]="+encodeURIComponent(s[c]);n.push(l)}else h+="="+encodeURIComponent(s),n.push(h)}}return 0===n.length?"":"?"+n.join("&")},parseQueryString:function(t){for(var e=t.split("&"),n={},r=0;r2&&"[]"===o.slice(s-2)&&(h=!0,o=o.slice(0,s-2),n[o]||(n[o]=[])),i=a[1]?C(a[1]):""),h?n[o].push(i):n[o]=i}return n},recognize:function(t){var e,n,r,i,a=[this.rootState],o={},s=!1;if(i=t.indexOf("?"),-1!==i){var h=t.substr(i+1,t.length);t=t.substr(0,i),o=this.parseQueryString(h)}for(t=decodeURI(t),"/"!==t.charAt(0)&&(t="/"+t),e=t.length,e>1&&"/"===t.charAt(e-1)&&(t=t.substr(0,e-1),s=!0),n=0,r=t.length;r>n&&(a=v(a,t.charAt(n)),a.length);n++);var u=[];for(n=0,r=a.length;r>n;n++)a[n].handlers&&u.push(a[n]);a=y(u);var c=u[0];return c&&c.handlers?(s&&"(.+)$"===c.regex.source.slice(-5)&&(t+="/"),w(c,t,o)):void 0}},E.prototype.map=S,E.VERSION="0.1.5";var O=E;n(3).amd?(r=function(){return O}.call(e,n,e,t),!(void 0!==r&&(t.exports=r))):"undefined"!=typeof t&&t.exports?t.exports=O:"undefined"!=typeof this&&(this.RouteRecognizer=O)}).call(this)}).call(e,n(4)(t))},function(t,e,n){t.exports=function(){throw new Error("define cannot be used indirect")}},function(t,e,n){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},function(e,n,r){e.exports=t},function(t,n,r){t.exports=e}])}); \ No newline at end of file diff --git a/modules/components/Switcher.js b/modules/components/Switcher.js index 8711a84..6e84048 100644 --- a/modules/components/Switcher.js +++ b/modules/components/Switcher.js @@ -9,21 +9,16 @@ export default class Switcher extends Component { this.getLocation = this.getLocation.bind(this); this.handleRouteChange = this.handleRouteChange.bind(this); this.getSwitch = this.getSwitch.bind(this); + this.initializeRecognizer = this.initializeRecognizer.bind(this); - this.defaultSwitch = this.props.defaultHandler ? React.createElement(this.props.defaultHandler, this.props.defaultHandlerProps) : null; - this.recognizer = new Recognizer(); - var children = [].concat(this.props.children); - children.forEach((child) => { - this.recognizer.add([{ - path: `${this.props.basePath}${child.props.path}`, - handler: child.props.handler || child - }]); - }); + this.defaultSwitch = props.defaultHandler ? React.createElement(props.defaultHandler, props.defaultHandlerProps) : null; // set initial state this.state = { visibleSwitch: null }; + + this.initializeRecognizer(props); } componentDidMount() { @@ -50,6 +45,27 @@ export default class Switcher extends Component { } } + componentWillReceiveProps(nextProps) { + this.initializeRecognizer(nextProps); + } + + initializeRecognizer(props) { + this.recognizer = new Recognizer(); + var children = [].concat(props.children); + children.forEach((child) => { + this.recognizer.add([{ + path: `${props.basePath}${child.props.path}`, + handler: child.props.handler || child + }]); + }); + var currentPath = this.getLocation(); + var switchElement = this.getSwitch(currentPath); + + this.setState({ + visibleSwitch: switchElement + }); + } + getLocation() { var location = decodeURI(window.location[this.props.location].slice(1).split('?')[0]); if(location.charAt(0) !== '/') { @@ -66,8 +82,8 @@ export default class Switcher extends Component { } handleRouteChange(e) { - var currentPath = this.getLocation(), - switchElement = this.getSwitch(currentPath); + var currentPath = this.getLocation(); + var switchElement = this.getSwitch(currentPath); if(typeof this.props.onChange === 'function') { this.props.onChange(!!switchElement, currentPath); diff --git a/test/components/Switcher_test.js b/test/components/Switcher_test.js index 26ab00c..fd35fd3 100644 --- a/test/components/Switcher_test.js +++ b/test/components/Switcher_test.js @@ -11,7 +11,6 @@ class Handler extends Component { } describe('Switcher', function() { - describe('#getLocation', function() { describe('using location.hash', function() { beforeEach(function() { @@ -301,5 +300,4 @@ describe('Switcher', function() { }); }); }); - });