Skip to content

Commit 88a9c61

Browse files
committed
Complete rewrite! Supports react-async-bootstrapper. Simplified API! Read docs!
1 parent 39a9ef5 commit 88a9c61

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2718
-873
lines changed

.babelrc

+9
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,29 @@
44
"stage-3",
55
"react"
66
],
7+
"plugins": [
8+
"transform-class-properties"
9+
],
710
"env": {
811
"commonjs": {
912
"presets": [
1013
"latest",
1114
"stage-3",
1215
"react"
1316
],
17+
"plugins": [
18+
"transform-class-properties"
19+
]
1420
},
1521
"umd": {
1622
"presets": [
1723
["latest", { "es2015": { "modules": false } }],
1824
"stage-3",
1925
"react"
2026
],
27+
"plugins": [
28+
"transform-class-properties"
29+
]
2130
}
2231
}
2332
}

.gitignore

-5
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ npm-debug.log
1111
# Jest
1212
coverage
1313

14-
# Build output
15-
umd
16-
commonjs
17-
build
18-
1914
# Flow
2015
flow-coverage
2116
flow-typed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# react-jobs 💼
22

3-
Asynchronously resolve data for your componets, with support for server side rendering.
3+
Asynchronously resolve data for your components, with support for server side rendering.
44

55
[![npm](https://img.shields.io/npm/v/react-jobs.svg?style=flat-square)](http://npm.im/react-jobs)
66
[![MIT License](https://img.shields.io/npm/l/react-jobs.svg?style=flat-square)](http://opensource.org/licenses/MIT)

commonjs/JobProvider.js

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
'use strict';
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
7+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8+
9+
var _react = require('react');
10+
11+
var _react2 = _interopRequireDefault(_react);
12+
13+
var _createJobContext = require('./createJobContext');
14+
15+
var _createJobContext2 = _interopRequireDefault(_createJobContext);
16+
17+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18+
19+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
20+
21+
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
22+
23+
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
24+
25+
var JobProvider = function (_Component) {
26+
_inherits(JobProvider, _Component);
27+
28+
function JobProvider() {
29+
_classCallCheck(this, JobProvider);
30+
31+
return _possibleConstructorReturn(this, (JobProvider.__proto__ || Object.getPrototypeOf(JobProvider)).apply(this, arguments));
32+
}
33+
34+
_createClass(JobProvider, [{
35+
key: 'componentWillMount',
36+
value: function componentWillMount() {
37+
this.jobContext = this.props.jobContext || (0, _createJobContext2.default)();
38+
this.rehydrateState = this.props.rehydrateState;
39+
}
40+
}, {
41+
key: 'getChildContext',
42+
value: function getChildContext() {
43+
var _this2 = this;
44+
45+
return {
46+
jobs: {
47+
getNextId: this.jobContext.getNextId,
48+
register: this.jobContext.register,
49+
get: this.jobContext.get,
50+
getRehydrate: function getRehydrate(id) {
51+
var rehydration = _this2.rehydrateState.jobs[id];
52+
delete _this2.rehydrateState.jobs[id];
53+
return rehydration;
54+
}
55+
}
56+
};
57+
}
58+
}, {
59+
key: 'render',
60+
value: function render() {
61+
return _react2.default.Children.only(this.props.children);
62+
}
63+
}]);
64+
65+
return JobProvider;
66+
}(_react.Component);
67+
68+
JobProvider.propTypes = {
69+
children: _react.PropTypes.node.isRequired,
70+
jobContext: _react.PropTypes.shape({
71+
getNextId: _react.PropTypes.func.isRequired,
72+
register: _react.PropTypes.func.isRequired,
73+
get: _react.PropTypes.func.isRequired,
74+
getState: _react.PropTypes.func.isRequired
75+
}),
76+
rehydrateState: _react.PropTypes.shape({
77+
jobs: _react.PropTypes.object.isRequired
78+
})
79+
};
80+
JobProvider.defaultProps = {
81+
jobContext: null,
82+
rehydrateState: {
83+
jobs: {}
84+
}
85+
};
86+
JobProvider.childContextTypes = {
87+
jobs: _react.PropTypes.shape({
88+
getNextId: _react.PropTypes.func.isRequired,
89+
register: _react.PropTypes.func.isRequired,
90+
get: _react.PropTypes.func.isRequired,
91+
getRehydrate: _react2.default.PropTypes.func.isRequired
92+
}).isRequired
93+
};
94+
exports.default = JobProvider;

commonjs/__tests__/withJob.test.js

+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
'use strict';
2+
3+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
4+
5+
var _react = require('react');
6+
7+
var _react2 = _interopRequireDefault(_react);
8+
9+
var _enzyme = require('enzyme');
10+
11+
var _helpers = require('../../tools/tests/helpers');
12+
13+
var _withJob = require('../withJob');
14+
15+
var _withJob2 = _interopRequireDefault(_withJob);
16+
17+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18+
19+
var workTime = 10; // ms
20+
21+
describe('withJob()', function () {
22+
(0, _helpers.warningsToErrors)();
23+
24+
describe('arguments', function () {
25+
it('returns a function', function () {
26+
var actual = _typeof((0, _withJob2.default)(function () {
27+
return undefined;
28+
}));
29+
var expected = 'function';
30+
expect(actual).toEqual(expected);
31+
});
32+
33+
it('should throws if no work is provided', function () {
34+
// $FlowIgnore: we expect this to flow error
35+
expect(function () {
36+
return (0, _withJob2.default)();
37+
}).toThrowError('You must provide a "work" function to the "withJob".');
38+
});
39+
40+
it('should throws if the work is invalid', function () {
41+
// $FlowIgnore: we expect this to flow error
42+
expect(function () {
43+
return (0, _withJob2.default)(1);
44+
}).toThrowError('You must provide a "work" function to the "withJob".');
45+
});
46+
});
47+
48+
describe('higher order component', function () {
49+
var hoc = (0, _withJob2.default)(function () {
50+
return (0, _helpers.resolveAfter)(1);
51+
});
52+
var Actual = hoc(_helpers.Foo);
53+
54+
it('should return a renderable component', function () {
55+
expect(function () {
56+
return (0, _enzyme.mount)(_react2.default.createElement(Actual, null));
57+
}).not.toThrowError();
58+
});
59+
});
60+
61+
describe('rendering', function () {
62+
it('should set the "result" immediately if the work does not return a promise', function () {
63+
var FooWithJob = (0, _withJob2.default)(function () {
64+
return 'bob';
65+
})(_helpers.Foo);
66+
expect((0, _enzyme.mount)(_react2.default.createElement(FooWithJob, null))).toMatchSnapshot();
67+
});
68+
69+
it('should provide the props to the work function', function () {
70+
var expected = { foo: 'bar', baz: 'qux' };
71+
var actual = void 0;
72+
var FooWithJob = (0, _withJob2.default)(function (props) {
73+
actual = props;
74+
})(_helpers.Foo);
75+
(0, _enzyme.mount)(_react2.default.createElement(FooWithJob, expected));
76+
expect(actual).toMatchObject(expected);
77+
});
78+
79+
it('should set "inProgress" when processing work', function () {
80+
var FooWithJob = (0, _withJob2.default)(function () {
81+
return (0, _helpers.resolveAfter)(workTime);
82+
})(_helpers.Foo);
83+
var actual = (0, _enzyme.mount)(_react2.default.createElement(FooWithJob, null)).find(_helpers.Foo).props();
84+
var expected = { job: { completed: false, inProgress: true } };
85+
expect(actual).toMatchObject(expected);
86+
});
87+
88+
it('should set "result" when work completes successfully', function () {
89+
var FooWithJob = (0, _withJob2.default)(function () {
90+
return (0, _helpers.resolveAfter)(workTime, 'result');
91+
})(_helpers.Foo);
92+
var renderWrapper = (0, _enzyme.mount)(_react2.default.createElement(FooWithJob, null));
93+
// Allow enough time for work to complete
94+
return (0, _helpers.resolveAfter)(workTime + 5).then(function () {
95+
var actual = renderWrapper.find(_helpers.Foo).props();
96+
var expected = { job: { completed: true, inProgress: false, result: 'result' } };
97+
expect(actual).toMatchObject(expected);
98+
})
99+
// swallow other errors
100+
.catch(function () {
101+
return undefined;
102+
});
103+
});
104+
105+
it('should set "error" when asynchronous work fails', function () {
106+
var error = new Error('poop');
107+
var FooWithJob = (0, _withJob2.default)(function () {
108+
return (0, _helpers.rejectAfter)(workTime, error);
109+
})(_helpers.Foo);
110+
var renderWrapper = (0, _enzyme.mount)(_react2.default.createElement(FooWithJob, null));
111+
// Allow enough time for work to complete
112+
return (0, _helpers.resolveAfter)(workTime + 5).then(function () {
113+
var actual = renderWrapper.find(_helpers.Foo).props();
114+
var expected = { job: { completed: true, inProgress: false, error: error } };
115+
expect(actual).toMatchObject(expected);
116+
})
117+
// swallow other errors
118+
.catch(function () {
119+
return undefined;
120+
});
121+
});
122+
123+
it('should set "error" when synchronous work fails', function () {
124+
var error = new Error('poop');
125+
var FooWithJob = (0, _withJob2.default)(function () {
126+
throw error;
127+
})(_helpers.Foo);
128+
var renderWrapper = (0, _enzyme.mount)(_react2.default.createElement(FooWithJob, null));
129+
var actual = renderWrapper.find(_helpers.Foo).props();
130+
var expected = { job: { completed: true, inProgress: false, error: error } };
131+
expect(actual).toMatchObject(expected);
132+
});
133+
134+
it('should not fire again when no "config.shouldWorkAgain" is provided', function () {
135+
var fireCount = 0;
136+
var Component = (0, _withJob2.default)(function () {
137+
fireCount += 1;
138+
return 'foo';
139+
})(function () {
140+
return _react2.default.createElement(
141+
'div',
142+
null,
143+
'bob'
144+
);
145+
});
146+
var renderWrapper = (0, _enzyme.mount)(_react2.default.createElement(Component, { foo: 'foo' }));
147+
expect(fireCount).toEqual(1);
148+
// Set props to cause a re-render
149+
renderWrapper.setProps({ foo: 'bar' });
150+
expect(fireCount).toEqual(1);
151+
});
152+
153+
it('should fire again for a remount', function () {
154+
var fireCount = 0;
155+
var Component = (0, _withJob2.default)(function () {
156+
fireCount += 1;
157+
return true;
158+
})(function () {
159+
return _react2.default.createElement(
160+
'div',
161+
null,
162+
'bob'
163+
);
164+
});
165+
(0, _enzyme.mount)(_react2.default.createElement(Component, null));
166+
expect(fireCount).toEqual(1);
167+
(0, _enzyme.mount)(_react2.default.createElement(Component, null));
168+
expect(fireCount).toEqual(2);
169+
});
170+
171+
it('should fire expectantly for a "shouldWorkAgain" implementation', function () {
172+
var prevProductIds = [];
173+
var fireCount = 0;
174+
var Component = (0, _withJob2.default)(function (_ref) {
175+
var productId = _ref.productId;
176+
177+
prevProductIds.push(productId);
178+
fireCount += 1;
179+
return true;
180+
}, {
181+
shouldWorkAgain: function shouldWorkAgain(prevProps, nextProps, currentJob) {
182+
return (currentJob.inProgress || currentJob.completed) && prevProductIds.indexOf(nextProps.productId) === -1;
183+
}
184+
})(function () {
185+
return _react2.default.createElement(
186+
'div',
187+
null,
188+
'bob'
189+
);
190+
});
191+
var renderWrapper = (0, _enzyme.mount)(_react2.default.createElement(Component, { productId: 1 }));
192+
expect(fireCount).toEqual(1);
193+
renderWrapper.setProps({ productId: 2 });
194+
expect(fireCount).toEqual(2);
195+
renderWrapper.setProps({ productId: 3 });
196+
expect(fireCount).toEqual(3);
197+
renderWrapper.setProps({ productId: 2 });
198+
expect(fireCount).toEqual(3);
199+
renderWrapper.setProps({ productId: 1 });
200+
expect(fireCount).toEqual(3);
201+
});
202+
});
203+
});

0 commit comments

Comments
 (0)