Skip to content
This repository was archived by the owner on May 23, 2018. It is now read-only.

Commit 4f5d7ad

Browse files
Bugfix: Immutable reducer updates state with mutable objects
1 parent 2acff8b commit 4f5d7ad

File tree

5 files changed

+146
-26
lines changed

5 files changed

+146
-26
lines changed

dist/es/immutable/reducer.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as actionTypes from '../lib/actionTypes';
2-
import { Record } from 'immutable';
2+
import { Record, fromJS } from 'immutable';
33

44
// eslint-disable-next-line new-cap
55
var State = Record({
@@ -15,13 +15,13 @@ function router5Reducer() {
1515

1616
switch (action.type) {
1717
case actionTypes.TRANSITION_START:
18-
return state.set('transitionRoute', action.payload.route).set('transitionError', null);
18+
return state.set('transitionRoute', fromJS(action.payload.route)).set('transitionError', null);
1919

2020
case actionTypes.TRANSITION_SUCCESS:
21-
return state.set('transitionRoute', null).set('transitionError', null).set('previousRoute', action.payload.previousRoute).set('route', action.payload.route);
21+
return state.set('transitionRoute', null).set('transitionError', null).set('previousRoute', fromJS(action.payload.previousRoute)).set('route', fromJS(action.payload.route));
2222

2323
case actionTypes.TRANSITION_ERROR:
24-
return state.set('transitionRoute', action.payload.route).set('transitionError', action.payload.transitionError);
24+
return state.set('transitionRoute', fromJS(action.payload.route)).set('transitionError', fromJS(action.payload.transitionError));
2525

2626
case actionTypes.CLEAR_ERRORS:
2727
return state.set('transitionRoute', null).set('transitionError', null);

dist/umd/redux-router5.js

Lines changed: 125 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ var actionTypes = Object.freeze({
2525
});
2626

2727
function navigateTo(name) {
28-
var params = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
29-
var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
28+
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
29+
var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
3030

3131
return {
3232
type: NAVIGATE_TO,
@@ -143,10 +143,10 @@ var actions = Object.freeze({
143143
return function (action) {
144144
switch (action.type) {
145145
case NAVIGATE_TO:
146-
var _action$payload = action.payload;
147-
var name = _action$payload.name;
148-
var params = _action$payload.params;
149-
var opts = _action$payload.opts;
146+
var _action$payload = action.payload,
147+
name = _action$payload.name,
148+
params = _action$payload.params,
149+
opts = _action$payload.opts;
150150

151151
router.navigate(name, params, opts);
152152
break;
@@ -174,9 +174,122 @@ var actions = Object.freeze({
174174
var typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
175175
return typeof obj;
176176
} : function (obj) {
177-
return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj;
177+
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
178178
};
179179

180+
var asyncGenerator = function () {
181+
function AwaitValue(value) {
182+
this.value = value;
183+
}
184+
185+
function AsyncGenerator(gen) {
186+
var front, back;
187+
188+
function send(key, arg) {
189+
return new Promise(function (resolve, reject) {
190+
var request = {
191+
key: key,
192+
arg: arg,
193+
resolve: resolve,
194+
reject: reject,
195+
next: null
196+
};
197+
198+
if (back) {
199+
back = back.next = request;
200+
} else {
201+
front = back = request;
202+
resume(key, arg);
203+
}
204+
});
205+
}
206+
207+
function resume(key, arg) {
208+
try {
209+
var result = gen[key](arg);
210+
var value = result.value;
211+
212+
if (value instanceof AwaitValue) {
213+
Promise.resolve(value.value).then(function (arg) {
214+
resume("next", arg);
215+
}, function (arg) {
216+
resume("throw", arg);
217+
});
218+
} else {
219+
settle(result.done ? "return" : "normal", result.value);
220+
}
221+
} catch (err) {
222+
settle("throw", err);
223+
}
224+
}
225+
226+
function settle(type, value) {
227+
switch (type) {
228+
case "return":
229+
front.resolve({
230+
value: value,
231+
done: true
232+
});
233+
break;
234+
235+
case "throw":
236+
front.reject(value);
237+
break;
238+
239+
default:
240+
front.resolve({
241+
value: value,
242+
done: false
243+
});
244+
break;
245+
}
246+
247+
front = front.next;
248+
249+
if (front) {
250+
resume(front.key, front.arg);
251+
} else {
252+
back = null;
253+
}
254+
}
255+
256+
this._invoke = send;
257+
258+
if (typeof gen.return !== "function") {
259+
this.return = undefined;
260+
}
261+
}
262+
263+
if (typeof Symbol === "function" && Symbol.asyncIterator) {
264+
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
265+
return this;
266+
};
267+
}
268+
269+
AsyncGenerator.prototype.next = function (arg) {
270+
return this._invoke("next", arg);
271+
};
272+
273+
AsyncGenerator.prototype.throw = function (arg) {
274+
return this._invoke("throw", arg);
275+
};
276+
277+
AsyncGenerator.prototype.return = function (arg) {
278+
return this._invoke("return", arg);
279+
};
280+
281+
return {
282+
wrap: function (fn) {
283+
return function () {
284+
return new AsyncGenerator(fn.apply(this, arguments));
285+
};
286+
},
287+
await: function (value) {
288+
return new AwaitValue(value);
289+
}
290+
};
291+
}();
292+
180293
var _extends = Object.assign || function (target) {
181294
for (var i = 1; i < arguments.length; i++) {
182295
var source = arguments[i];
@@ -199,7 +312,7 @@ var actions = Object.freeze({
199312
};
200313

201314
function router5Reducer() {
202-
var state = arguments.length <= 0 || arguments[0] === undefined ? initialState : arguments[0];
315+
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState;
203316
var action = arguments[1];
204317

205318
switch (action.type) {
@@ -329,18 +442,17 @@ var actions = Object.freeze({
329442
}
330443

331444
function routeNodeSelector(routeNode) {
332-
var reducerKey = arguments.length <= 1 || arguments[1] === undefined ? 'router' : arguments[1];
445+
var reducerKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'router';
333446

334447
var routerStateSelector = function routerStateSelector(state) {
335448
return state[reducerKey] || state.get && state.get(reducerKey);
336449
};
337450
var lastReturnedValue = void 0;
338451

339452
return function (state) {
340-
var _routerStateSelector = routerStateSelector(state);
341-
342-
var route = _routerStateSelector.route;
343-
var previousRoute = _routerStateSelector.previousRoute;
453+
var _routerStateSelector = routerStateSelector(state),
454+
route = _routerStateSelector.route,
455+
previousRoute = _routerStateSelector.previousRoute;
344456

345457
var intersection = route ? transitionPath(route, previousRoute).intersection : '';
346458

modules/immutable/reducer.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as actionTypes from '../lib/actionTypes';
2-
import { Record } from 'immutable';
2+
import { Record, fromJS } from 'immutable';
33

44
// eslint-disable-next-line new-cap
55
const State = Record({
@@ -13,20 +13,20 @@ function router5Reducer(state = new State(), action) {
1313
switch (action.type) {
1414
case actionTypes.TRANSITION_START:
1515
return state
16-
.set('transitionRoute', action.payload.route)
16+
.set('transitionRoute', fromJS(action.payload.route))
1717
.set('transitionError', null);
1818

1919
case actionTypes.TRANSITION_SUCCESS:
2020
return state
2121
.set('transitionRoute', null)
2222
.set('transitionError', null)
23-
.set('previousRoute', action.payload.previousRoute)
24-
.set('route', action.payload.route);
23+
.set('previousRoute', fromJS(action.payload.previousRoute))
24+
.set('route', fromJS(action.payload.route));
2525

2626
case actionTypes.TRANSITION_ERROR:
2727
return state
28-
.set('transitionRoute', action.payload.route)
29-
.set('transitionError', action.payload.transitionError);
28+
.set('transitionRoute', fromJS(action.payload.route))
29+
.set('transitionError', fromJS(action.payload.transitionError));
3030

3131
case actionTypes.CLEAR_ERRORS:
3232
return state

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
},
3636
"homepage": "http://router5.github.com/router5/redux-router5",
3737
"devDependencies": {
38+
"babel-cli": "^6.24.1",
3839
"babel-core": "~6.9.1",
3940
"babel-eslint": "~6.0.4",
4041
"babel-plugin-transform-export-extensions": "~6.8.0",

test/main.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ describe('redux-router5', () => {
6363
getState: () => new State()
6464
}];
6565

66+
const unifiedEqual = (result, expectation) => {
67+
if (typeof result.toJS === 'function') {
68+
return expect(result.toJS()).to.eql(expectation);
69+
}
70+
return expect(result).to.equal(expectation);
71+
};
72+
6673
types.forEach((type) => {
6774

6875
let state;
@@ -71,20 +78,20 @@ describe('redux-router5', () => {
7178

7279
it(`[${type.name}] should handle transitionStart actions`, () => {
7380
state = type.reducer(state, actions.transitionStart(route2, route1));
74-
expect(state.transitionRoute).to.equal(route2);
81+
unifiedEqual(state.transitionRoute, route2);
7582
expect(state.transitionError).to.equal(null);
7683
});
7784

7885
it(`[${type.name}] should handle transitionSuccess actions`, () => {
7986
state = type.reducer(state, actions.transitionSuccess(route2, route1));
8087
expect(state.transitionRoute).to.equal(null);
8188
expect(state.transitionError).to.equal(null);
82-
expect(state.route).to.equal(route2);
89+
unifiedEqual(state.route, route2);
8390
});
8491

8592
it(`[${type.name}] should handle transitionError actions`, () => {
8693
state = type.reducer(state, actions.transitionError(route2, route1, 'ERR'));
87-
expect(state.transitionRoute).to.equal(route2);
94+
unifiedEqual(state.transitionRoute, route2);
8895
expect(state.transitionError).to.equal('ERR');
8996
});
9097

0 commit comments

Comments
 (0)