Skip to content

Commit ea6eaef

Browse files
author
Berkeley Martinez
committed
Merge pull request #67 from ThunderCatsJS/fix/infinite-loop
[fix] Prevent infinite loops
2 parents 3bf38d0 + 80afec7 commit ea6eaef

File tree

4 files changed

+93
-2
lines changed

4 files changed

+93
-2
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
# December 22, 2015 v0.5.1
2+
3+
This release fixes the following situation.
4+
5+
If an action were to take place during a `componentWillMount`, this
6+
will cause an infinite loop as the action triggers `combineLatest`
7+
to emit an event, which then triggers another `renderToString`
8+
which starts the whole thing over again.
9+
10+
There is a `first` filter in the stream, but internally the `onNext` of
11+
the observer is called before the `onCompleted` of the observer can be
12+
called, meaning the `first` filter never has a chance to call `onCompleted`
13+
14+
Moral of the story? Beware of Zalgo!
15+
16+
* [08411fe](../../commit/08411fe) [fix] Prevent infinite loops
17+
118
# December 20, 2015 v0.5.0
219

320
This release is a breaking change. It depends on internal API changes introduced

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "thundercats-react",
33
"description": "Thundercats addon for use with React",
4-
"version": "0.5.0",
4+
"version": "0.5.1",
55
"homepage": "http://thundercats.js.org",
66
"keywords": [
77
"alwaysUseTwoSpaces",

src/Render.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,16 @@ export function renderToString$(cat, Component) {
8282
}
8383

8484
return fetch$(fetchMap)
85+
// move fetch to next event loop to prevent
86+
// synchronous actions from causing infiniti loop
87+
.delay(50)
8588
.map((fetchMap) => {
8689
const markup = renderToString(Burrito);
8790
return {
8891
markup,
8992
fetchMap
9093
};
9194
})
92-
.first()
9395
.tapOnNext(() => cat.fetchMap = null);
9496
}
9597

test/render.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,78 @@ describe('renderToString$', function() {
230230
);
231231
});
232232

233+
it('should prevent infinite loops', done => {
234+
const element = React.createElement(createContainer(
235+
{
236+
actions: ['catActions'],
237+
store: 'catStore',
238+
fetchAction: 'catActions.doAction',
239+
getPayload: () => ({})
240+
},
241+
createClass({
242+
displayName: 'Test',
243+
propTypes: {
244+
catActions: React.PropTypes.object
245+
},
246+
componentWillMount() {
247+
this.props.catActions.doThis();
248+
},
249+
250+
render() {
251+
return React.createElement('h1', null, 'hello world');
252+
}
253+
})
254+
));
255+
256+
const CatActions = Actions({
257+
refs: { displayName: 'catActions' },
258+
doAction() {
259+
return Observable.just({ set: { foo: 'baz' } }).delay(500);
260+
},
261+
doThis() {
262+
return { set: { qux: 'quo' } };
263+
}
264+
});
265+
266+
const CatStore = Store({
267+
refs: {
268+
value: { foo: 'bar' },
269+
displayName: 'CatStore'
270+
},
271+
init({ instance: store, args: [cat] }) {
272+
const actions = cat.getActions('catActions');
273+
store.register(actions.doAction);
274+
store.register(actions.doThis);
275+
}
276+
});
277+
278+
279+
const cat = Cat()();
280+
281+
cat.register(CatActions);
282+
cat.register(CatStore, null, cat);
283+
284+
renderToString$(cat, element)
285+
.flatMap(
286+
dehydrate(cat),
287+
)
288+
.doOnNext(({ CatStore })=> {
289+
assert.equal(
290+
CatStore.foo,
291+
'baz',
292+
'foo did not equal baz'
293+
);
294+
295+
assert.isNotOk(CatStore.qux);
296+
})
297+
.subscribe(
298+
() => {},
299+
done,
300+
done
301+
);
302+
303+
});
304+
233305
it('should error on fetch errors', (done) => {
234306
renderToString$(cat, 'not the momma')
235307
.subscribe(

0 commit comments

Comments
 (0)