async-data-access
Advanced tools
Comparing version 0.0.1 to 0.1.0
@@ -1,4 +0,33 @@ | ||
import React, { Component } from 'react'; | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
var asyncToGenerator = function (fn) { | ||
return function () { | ||
var gen = fn.apply(this, arguments); | ||
return new Promise(function (resolve, reject) { | ||
function step(key, arg) { | ||
try { | ||
var info = gen[key](arg); | ||
var value = info.value; | ||
} catch (error) { | ||
reject(error); | ||
return; | ||
} | ||
if (info.done) { | ||
resolve(value); | ||
} else { | ||
return Promise.resolve(value).then(function (value) { | ||
step("next", value); | ||
}, function (err) { | ||
step("throw", err); | ||
}); | ||
} | ||
} | ||
return step("next"); | ||
}); | ||
}; | ||
}; | ||
var classCallCheck = function (instance, Constructor) { | ||
@@ -28,2 +57,16 @@ if (!(instance instanceof Constructor)) { | ||
var _extends = Object.assign || function (target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i]; | ||
for (var key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
var inherits = function (subClass, superClass) { | ||
@@ -53,32 +96,164 @@ if (typeof superClass !== "function" && superClass !== null) { | ||
var ExampleComponent = function (_Component) { | ||
inherits(ExampleComponent, _Component); | ||
var AsyncDataAccess = function (_React$Component) { | ||
inherits(AsyncDataAccess, _React$Component); | ||
function ExampleComponent() { | ||
classCallCheck(this, ExampleComponent); | ||
return possibleConstructorReturn(this, (ExampleComponent.__proto__ || Object.getPrototypeOf(ExampleComponent)).apply(this, arguments)); | ||
function AsyncDataAccess() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
classCallCheck(this, AsyncDataAccess); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = AsyncDataAccess.__proto__ || Object.getPrototypeOf(AsyncDataAccess)).call.apply(_ref, [this].concat(args))), _this), _this.state = { | ||
isFetching: false, | ||
payload: null, | ||
didInvalidate: false, | ||
lastFetchFailed: false, | ||
lastError: null | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(ExampleComponent, [{ | ||
createClass(AsyncDataAccess, [{ | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
this.doFetch(); | ||
} | ||
}, { | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate(prevProps) { | ||
// Trigger reload if our fetch has changed | ||
// TODO if we're only changing `transform` then we could skip re-isFetching | ||
// assuming we saved our pre-transformed payload | ||
if (this.props.fetch !== prevProps.fetch || this.props.transform !== prevProps.transform) { | ||
this.doFetch(); | ||
} | ||
} | ||
}, { | ||
key: 'doFetch', | ||
value: function () { | ||
var _ref2 = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { | ||
var res, payload, message; | ||
return regeneratorRuntime.wrap(function _callee$(_context) { | ||
while (1) { | ||
switch (_context.prev = _context.next) { | ||
case 0: | ||
res = void 0; | ||
payload = void 0; | ||
this.setState({ isFetching: true }); | ||
// TODO if this has been called as a reload then we should set `didInvalidate` to be true | ||
// Needs some more thought as to exact conditions under which that should happen | ||
// possibly it should instead just be a flag? | ||
_context.prev = 3; | ||
_context.next = 6; | ||
return this.props.fetch(this.props); | ||
case 6: | ||
res = _context.sent; | ||
if (!(res instanceof Error)) { | ||
_context.next = 9; | ||
break; | ||
} | ||
throw res; | ||
case 9: | ||
if (this.props.transform) { | ||
payload = this.props.transform(res); | ||
} else { | ||
payload = res; | ||
} | ||
_context.next = 19; | ||
break; | ||
case 12: | ||
_context.prev = 12; | ||
_context.t0 = _context['catch'](3); | ||
try { | ||
if (this.props.onError) { | ||
this.props.onError(_context.t0); | ||
} | ||
} catch (e) { | ||
// do nothing | ||
} | ||
message = void 0; | ||
// TODO change this to a transformError call | ||
if (_context.t0.response && _context.t0.response.data && _context.t0.response.data.length && _context.t0.response.data[0].description) { | ||
message = _context.t0.response.data[0].description; | ||
} else { | ||
message = _context.t0.message; | ||
} | ||
this.setState({ | ||
isFetching: false, | ||
didInvalidate: true, | ||
lastFetchFailed: true, | ||
lastError: message | ||
}); | ||
return _context.abrupt('return'); | ||
case 19: | ||
this.setState({ | ||
payload: payload, | ||
isFetching: false, | ||
didInvalidate: false, | ||
lastFetchFailed: false | ||
}); | ||
case 20: | ||
case 'end': | ||
return _context.stop(); | ||
} | ||
} | ||
}, _callee, this, [[3, 12]]); | ||
})); | ||
function doFetch() { | ||
return _ref2.apply(this, arguments); | ||
} | ||
return doFetch; | ||
}() | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var text = this.props.text; | ||
var _this2 = this; | ||
return React.createElement( | ||
'div', | ||
null, | ||
'Example Component: ', | ||
text | ||
); | ||
return this.props.children(_extends({}, this.state, { reload: function reload() { | ||
return _this2.doFetch(); | ||
} })); | ||
} | ||
}]); | ||
return ExampleComponent; | ||
}(Component); | ||
return AsyncDataAccess; | ||
}(React.Component); | ||
ExampleComponent.propTypes = { | ||
text: PropTypes.string | ||
AsyncDataAccess.propTypes = { | ||
children: PropTypes.func, | ||
// `fetch` is the method to fetch data | ||
fetch: PropTypes.func.isRequired, | ||
// `transform` method that can be used for transforms on fetched data, | ||
// the intention being that `fetch` can be simple and error handling done before transforming attempted | ||
transform: PropTypes.func, | ||
// `onError` handler that will be called if an error occurs | ||
onError: PropTypes.func | ||
}; | ||
AsyncDataAccess.defaultProps = { | ||
fetch: function fetch() { | ||
return null; | ||
} | ||
}; | ||
export default ExampleComponent; | ||
export default AsyncDataAccess; | ||
//# sourceMappingURL=index.es.js.map |
@@ -5,6 +5,34 @@ 'use strict'; | ||
var React = require('react'); | ||
var React__default = _interopDefault(React); | ||
var React = _interopDefault(require('react')); | ||
var PropTypes = _interopDefault(require('prop-types')); | ||
var asyncToGenerator = function (fn) { | ||
return function () { | ||
var gen = fn.apply(this, arguments); | ||
return new Promise(function (resolve, reject) { | ||
function step(key, arg) { | ||
try { | ||
var info = gen[key](arg); | ||
var value = info.value; | ||
} catch (error) { | ||
reject(error); | ||
return; | ||
} | ||
if (info.done) { | ||
resolve(value); | ||
} else { | ||
return Promise.resolve(value).then(function (value) { | ||
step("next", value); | ||
}, function (err) { | ||
step("throw", err); | ||
}); | ||
} | ||
} | ||
return step("next"); | ||
}); | ||
}; | ||
}; | ||
var classCallCheck = function (instance, Constructor) { | ||
@@ -34,2 +62,16 @@ if (!(instance instanceof Constructor)) { | ||
var _extends = Object.assign || function (target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i]; | ||
for (var key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
var inherits = function (subClass, superClass) { | ||
@@ -59,32 +101,164 @@ if (typeof superClass !== "function" && superClass !== null) { | ||
var ExampleComponent = function (_Component) { | ||
inherits(ExampleComponent, _Component); | ||
var AsyncDataAccess = function (_React$Component) { | ||
inherits(AsyncDataAccess, _React$Component); | ||
function ExampleComponent() { | ||
classCallCheck(this, ExampleComponent); | ||
return possibleConstructorReturn(this, (ExampleComponent.__proto__ || Object.getPrototypeOf(ExampleComponent)).apply(this, arguments)); | ||
function AsyncDataAccess() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
classCallCheck(this, AsyncDataAccess); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = AsyncDataAccess.__proto__ || Object.getPrototypeOf(AsyncDataAccess)).call.apply(_ref, [this].concat(args))), _this), _this.state = { | ||
isFetching: false, | ||
payload: null, | ||
didInvalidate: false, | ||
lastFetchFailed: false, | ||
lastError: null | ||
}, _temp), possibleConstructorReturn(_this, _ret); | ||
} | ||
createClass(ExampleComponent, [{ | ||
createClass(AsyncDataAccess, [{ | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
this.doFetch(); | ||
} | ||
}, { | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate(prevProps) { | ||
// Trigger reload if our fetch has changed | ||
// TODO if we're only changing `transform` then we could skip re-isFetching | ||
// assuming we saved our pre-transformed payload | ||
if (this.props.fetch !== prevProps.fetch || this.props.transform !== prevProps.transform) { | ||
this.doFetch(); | ||
} | ||
} | ||
}, { | ||
key: 'doFetch', | ||
value: function () { | ||
var _ref2 = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { | ||
var res, payload, message; | ||
return regeneratorRuntime.wrap(function _callee$(_context) { | ||
while (1) { | ||
switch (_context.prev = _context.next) { | ||
case 0: | ||
res = void 0; | ||
payload = void 0; | ||
this.setState({ isFetching: true }); | ||
// TODO if this has been called as a reload then we should set `didInvalidate` to be true | ||
// Needs some more thought as to exact conditions under which that should happen | ||
// possibly it should instead just be a flag? | ||
_context.prev = 3; | ||
_context.next = 6; | ||
return this.props.fetch(this.props); | ||
case 6: | ||
res = _context.sent; | ||
if (!(res instanceof Error)) { | ||
_context.next = 9; | ||
break; | ||
} | ||
throw res; | ||
case 9: | ||
if (this.props.transform) { | ||
payload = this.props.transform(res); | ||
} else { | ||
payload = res; | ||
} | ||
_context.next = 19; | ||
break; | ||
case 12: | ||
_context.prev = 12; | ||
_context.t0 = _context['catch'](3); | ||
try { | ||
if (this.props.onError) { | ||
this.props.onError(_context.t0); | ||
} | ||
} catch (e) { | ||
// do nothing | ||
} | ||
message = void 0; | ||
// TODO change this to a transformError call | ||
if (_context.t0.response && _context.t0.response.data && _context.t0.response.data.length && _context.t0.response.data[0].description) { | ||
message = _context.t0.response.data[0].description; | ||
} else { | ||
message = _context.t0.message; | ||
} | ||
this.setState({ | ||
isFetching: false, | ||
didInvalidate: true, | ||
lastFetchFailed: true, | ||
lastError: message | ||
}); | ||
return _context.abrupt('return'); | ||
case 19: | ||
this.setState({ | ||
payload: payload, | ||
isFetching: false, | ||
didInvalidate: false, | ||
lastFetchFailed: false | ||
}); | ||
case 20: | ||
case 'end': | ||
return _context.stop(); | ||
} | ||
} | ||
}, _callee, this, [[3, 12]]); | ||
})); | ||
function doFetch() { | ||
return _ref2.apply(this, arguments); | ||
} | ||
return doFetch; | ||
}() | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var text = this.props.text; | ||
var _this2 = this; | ||
return React__default.createElement( | ||
'div', | ||
null, | ||
'Example Component: ', | ||
text | ||
); | ||
return this.props.children(_extends({}, this.state, { reload: function reload() { | ||
return _this2.doFetch(); | ||
} })); | ||
} | ||
}]); | ||
return ExampleComponent; | ||
return AsyncDataAccess; | ||
}(React.Component); | ||
ExampleComponent.propTypes = { | ||
text: PropTypes.string | ||
AsyncDataAccess.propTypes = { | ||
children: PropTypes.func, | ||
// `fetch` is the method to fetch data | ||
fetch: PropTypes.func.isRequired, | ||
// `transform` method that can be used for transforms on fetched data, | ||
// the intention being that `fetch` can be simple and error handling done before transforming attempted | ||
transform: PropTypes.func, | ||
// `onError` handler that will be called if an error occurs | ||
onError: PropTypes.func | ||
}; | ||
AsyncDataAccess.defaultProps = { | ||
fetch: function fetch() { | ||
return null; | ||
} | ||
}; | ||
module.exports = ExampleComponent; | ||
module.exports = AsyncDataAccess; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "async-data-access", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "React component for asynchronously fetching data", | ||
@@ -20,2 +20,4 @@ "author": "stevesims", | ||
"test": "cross-env CI=1 react-scripts test --env=jsdom", | ||
"test:coverage": "react-scripts test --env=jsdom --coverage", | ||
"test:debug": "react-scripts --inspect-brk test --runInBand --env=jsdom", | ||
"test:watch": "react-scripts test --env=jsdom", | ||
@@ -41,2 +43,4 @@ "build": "rollup -c", | ||
"cross-env": "^5.1.4", | ||
"enzyme": "^3.6.0", | ||
"enzyme-adapter-react-16": "^1.5.0", | ||
"eslint": "^5.0.1", | ||
@@ -43,0 +47,0 @@ "eslint-config-standard": "^11.0.0", |
@@ -15,11 +15,71 @@ # async-data-access | ||
This component will load data asynchronously using the provided `fetch` function, and pass through status values and payload via the render-props pattern to a provided child render method. An optional `transform` function can be given to transform the fetched response into a different format. | ||
AsyncDataAccess is agnostic about how data is fetched, with only the expectation that the fetching will be performed asynchronously. This means that it can be written using a modern `async` function, or alternatively using a `Promise`. The fetch method can potentially make multiple API calls and gather their results together. | ||
Props accepted by this component are: | ||
```js | ||
{ | ||
// `fetch` is the method to fetch data | ||
fetch: PropTypes.func.isRequired, | ||
// `transform` method that can be used for transforms on fetched data, | ||
// the intention being that `fetch` can be simple and error handling done before transforming attempted | ||
transform: PropTypes.func, | ||
// `onError` handler that will be called if an error occurs | ||
onError: PropTypes.func, | ||
} | ||
``` | ||
Props passed through to render child, with their types are: | ||
```js | ||
{ | ||
isFetching: PropTypes.bool, // Flag to indicate fetch is in progress | ||
didInvalidate: PropTypes.bool, // Flag that indicates current fetch invalidated payload | ||
lastFetchFailed: PropTypes.bool, // Flag that indicates last fetch failed | ||
lastError: PropTypes.string, // String representing last error message received | ||
payload: PropTypes.any, // Payload from fetch, defaults to null | ||
reload: PropTypes.func // Function to trigger a reload | ||
} | ||
``` | ||
### Example | ||
```jsx | ||
import React, { Component } from 'react' | ||
import MyComponent from 'async-data-access' | ||
import AsyncDataAccess from 'async-data-access' | ||
class Example extends Component { | ||
fetcher = async () => { | ||
// fetch data asynchronously from a server | ||
return await fetch('http://example.com/movies.json') | ||
} | ||
transformer (response) { | ||
// transform response to a different format (if required) | ||
return response.movieList | ||
} | ||
render () { | ||
return ( | ||
<MyComponent /> | ||
<AsyncDataAccess fetch={this.fetcher} transform={this.transformer}> | ||
{ | ||
props => { | ||
const { isFetching, payload } = props; | ||
const movies = payload || []; | ||
return <> | ||
{ isFetching && <h2>Loading data</h2> } | ||
{ | ||
movies.map( | ||
({ title, description }) => <> | ||
<h3>{title}</h3> | ||
<div>{description}</div> | ||
</> | ||
) | ||
} | ||
</> | ||
} | ||
} | ||
</AsyncDataAccess> | ||
) | ||
@@ -26,0 +86,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
29171
434
92
27
1