diff --git a/.eslintrc b/.eslintrc index de8925d..56dedae 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,6 +7,7 @@ "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", "react/jsx-filename-extension": 0, + "import/prefer-default-export": 0, "no-unused-expressions": [ 2,{ "allowShortCircuit": true, }], diff --git a/demo/components/UseTimerDemo.js b/demo/components/UseTimerDemo.js index 543e32a..28cd1f6 100644 --- a/demo/components/UseTimerDemo.js +++ b/demo/components/UseTimerDemo.js @@ -13,8 +13,7 @@ export default function UseTimerDemo({ expiryTimestamp }: Object) { pause, resume, restart, - } = useTimer({ expiryTimestamp, onExpire: () => console.warn('onExpire called') }); - + } = useTimer({ expiryTimestamp, autoStart: true, onExpire: () => console.warn('onExpire called') }); return (
diff --git a/docs/index.js b/docs/index.js index b4a6c41..0b4cfff 100644 --- a/docs/index.js +++ b/docs/index.js @@ -166,7 +166,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = UseTimerDemo;\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _index = __webpack_require__(/*! ../../src/index */ \"./src/index.js\");\n\nvar _TimerStyled = __webpack_require__(/*! ./TimerStyled */ \"./demo/components/TimerStyled.js\");\n\nvar _TimerStyled2 = _interopRequireDefault(_TimerStyled);\n\nvar _Button = __webpack_require__(/*! ./Button */ \"./demo/components/Button.js\");\n\nvar _Button2 = _interopRequireDefault(_Button);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction UseTimerDemo(_ref) {\n var expiryTimestamp = _ref.expiryTimestamp;\n\n var _useTimer = (0, _index.useTimer)({ expiryTimestamp: expiryTimestamp, onExpire: function onExpire() {\n return console.warn('onExpire called');\n } }),\n seconds = _useTimer.seconds,\n minutes = _useTimer.minutes,\n hours = _useTimer.hours,\n days = _useTimer.days,\n start = _useTimer.start,\n pause = _useTimer.pause,\n resume = _useTimer.resume,\n restart = _useTimer.restart;\n\n return _react2.default.createElement(\n 'div',\n null,\n _react2.default.createElement(\n 'h2',\n null,\n 'UseTimer Demo'\n ),\n _react2.default.createElement(_TimerStyled2.default, { seconds: seconds, minutes: minutes, hours: hours, days: days }),\n _react2.default.createElement(\n _Button2.default,\n { type: 'button', onClick: start },\n 'Start'\n ),\n _react2.default.createElement(\n _Button2.default,\n { type: 'button', onClick: pause },\n 'Pause'\n ),\n _react2.default.createElement(\n _Button2.default,\n { type: 'button', onClick: resume },\n 'Resume'\n ),\n _react2.default.createElement(\n _Button2.default,\n {\n type: 'button',\n onClick: function onClick() {\n // Restarts to 10 minutes timer\n var time = new Date();\n time.setSeconds(time.getSeconds() + 600);\n restart(time);\n }\n },\n 'Restart'\n )\n );\n}\n\n//# sourceURL=webpack:///./demo/components/UseTimerDemo.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = UseTimerDemo;\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _index = __webpack_require__(/*! ../../src/index */ \"./src/index.js\");\n\nvar _TimerStyled = __webpack_require__(/*! ./TimerStyled */ \"./demo/components/TimerStyled.js\");\n\nvar _TimerStyled2 = _interopRequireDefault(_TimerStyled);\n\nvar _Button = __webpack_require__(/*! ./Button */ \"./demo/components/Button.js\");\n\nvar _Button2 = _interopRequireDefault(_Button);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction UseTimerDemo(_ref) {\n var expiryTimestamp = _ref.expiryTimestamp;\n\n var _useTimer = (0, _index.useTimer)({ expiryTimestamp: expiryTimestamp, autoStart: false, onExpire: function onExpire() {\n return console.warn('onExpire called');\n } }),\n seconds = _useTimer.seconds,\n minutes = _useTimer.minutes,\n hours = _useTimer.hours,\n days = _useTimer.days,\n start = _useTimer.start,\n pause = _useTimer.pause,\n resume = _useTimer.resume,\n restart = _useTimer.restart;\n\n return _react2.default.createElement(\n 'div',\n null,\n _react2.default.createElement(\n 'h2',\n null,\n 'UseTimer Demo'\n ),\n _react2.default.createElement(_TimerStyled2.default, { seconds: seconds, minutes: minutes, hours: hours, days: days }),\n _react2.default.createElement(\n _Button2.default,\n { type: 'button', onClick: start },\n 'Start'\n ),\n _react2.default.createElement(\n _Button2.default,\n { type: 'button', onClick: pause },\n 'Pause'\n ),\n _react2.default.createElement(\n _Button2.default,\n { type: 'button', onClick: resume },\n 'Resume'\n ),\n _react2.default.createElement(\n _Button2.default,\n {\n type: 'button',\n onClick: function onClick() {\n // Restarts to 10 minutes timer\n var time = new Date();\n time.setSeconds(time.getSeconds() + 600);\n restart(time);\n }\n },\n 'Restart'\n )\n );\n}\n\n//# sourceURL=webpack:///./demo/components/UseTimerDemo.js?"); /***/ }), @@ -443,6 +443,30 @@ eval("var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn th /***/ }), +/***/ "./src/hooks/index.js": +/*!****************************!*\ + !*** ./src/hooks/index.js ***! + \****************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.useInterval = undefined;\n\nvar _useInterval = __webpack_require__(/*! ./useInterval */ \"./src/hooks/useInterval.js\");\n\nvar _useInterval2 = _interopRequireDefault(_useInterval);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.useInterval = _useInterval2.default;\n\n//# sourceURL=webpack:///./src/hooks/index.js?"); + +/***/ }), + +/***/ "./src/hooks/useInterval.js": +/*!**********************************!*\ + !*** ./src/hooks/useInterval.js ***! + \**********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = useInterval;\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nfunction useInterval(callback, delay) {\n var callbacRef = (0, _react.useRef)();\n\n // update callback function with current render callback that has access to latest props and state\n (0, _react.useEffect)(function () {\n callbacRef.current = callback;\n });\n\n (0, _react.useEffect)(function () {\n if (!delay) {\n return function () {};\n }\n\n var interval = setInterval(function () {\n callbacRef.current && callbacRef.current();\n }, delay);\n return function () {\n return clearInterval(interval);\n };\n }, [delay]);\n}\n\n//# sourceURL=webpack:///./src/hooks/useInterval.js?"); + +/***/ }), + /***/ "./src/index.js": /*!**********************!*\ !*** ./src/index.js ***! @@ -463,7 +487,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _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; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nexports.default = useStopwatch;\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _utils = __webpack_require__(/*! ./utils */ \"./src/utils/index.js\");\n\nfunction useStopwatch(settings) {\n var _ref = settings || {},\n autoStart = _ref.autoStart,\n offsetTimestamp = _ref.offsetTimestamp;\n\n var _useState = (0, _react.useState)(_utils.Time.getSecondsFromExpiry(offsetTimestamp || 0)),\n _useState2 = _slicedToArray(_useState, 2),\n seconds = _useState2[0],\n setSeconds = _useState2[1];\n\n var _useState3 = (0, _react.useState)(autoStart),\n _useState4 = _slicedToArray(_useState3, 2),\n isRunning = _useState4[0],\n setIsRunning = _useState4[1];\n\n var intervalRef = (0, _react.useRef)();\n\n function clearIntervalRef() {\n if (intervalRef.current) {\n setIsRunning(false);\n clearInterval(intervalRef.current);\n intervalRef.current = undefined;\n }\n }\n\n function start() {\n if (!intervalRef.current) {\n setIsRunning(true);\n intervalRef.current = setInterval(function () {\n return setSeconds(function (prevSeconds) {\n return prevSeconds + 1;\n });\n }, 1000);\n }\n }\n\n function pause() {\n clearIntervalRef();\n }\n\n function reset(offset) {\n clearIntervalRef();\n setSeconds(_utils.Time.getSecondsFromExpiry(offset || 0));\n if (autoStart) {\n start();\n }\n }\n\n // didMount effect\n (0, _react.useEffect)(function () {\n if (autoStart) {\n start();\n }\n return clearIntervalRef;\n }, []);\n\n return _extends({}, _utils.Time.getTimeFromSeconds(seconds), { start: start, pause: pause, reset: reset, isRunning: isRunning\n });\n}\n\n//# sourceURL=webpack:///./src/useStopwatch.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _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; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nexports.default = useStopwatch;\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _utils = __webpack_require__(/*! ./utils */ \"./src/utils/index.js\");\n\nvar _hooks = __webpack_require__(/*! ./hooks */ \"./src/hooks/index.js\");\n\nfunction useStopwatch(_ref) {\n var autoStart = _ref.autoStart,\n offsetTimestamp = _ref.offsetTimestamp;\n\n var _useState = (0, _react.useState)(_utils.Time.getSecondsFromExpiry(offsetTimestamp || 0)),\n _useState2 = _slicedToArray(_useState, 2),\n seconds = _useState2[0],\n setSeconds = _useState2[1];\n\n var _useState3 = (0, _react.useState)(autoStart),\n _useState4 = _slicedToArray(_useState3, 2),\n isRunning = _useState4[0],\n setIsRunning = _useState4[1];\n\n (0, _hooks.useInterval)(function () {\n setSeconds(function (prevSeconds) {\n return prevSeconds + 1;\n });\n }, isRunning ? 1000 : null);\n\n function start() {\n setIsRunning(true);\n }\n\n function pause() {\n setIsRunning(false);\n }\n\n function reset(offset) {\n setIsRunning(autoStart);\n setSeconds(_utils.Time.getSecondsFromExpiry(offset || 0));\n }\n\n return _extends({}, _utils.Time.getTimeFromSeconds(seconds), { start: start, pause: pause, reset: reset, isRunning: isRunning\n });\n}\n\n//# sourceURL=webpack:///./src/useStopwatch.js?"); /***/ }), @@ -475,7 +499,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _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; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nexports.default = useTime;\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _utils = __webpack_require__(/*! ./utils */ \"./src/utils/index.js\");\n\nfunction useTime(settings) {\n var _ref = settings || {},\n format = _ref.format;\n\n var _useState = (0, _react.useState)(_utils.Time.getSecondsFromTimeNow()),\n _useState2 = _slicedToArray(_useState, 2),\n seconds = _useState2[0],\n setSeconds = _useState2[1];\n\n var intervalRef = (0, _react.useRef)();\n\n function clearIntervalRef() {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = undefined;\n }\n }\n\n function start() {\n if (!intervalRef.current) {\n intervalRef.current = setInterval(function () {\n return setSeconds(_utils.Time.getSecondsFromTimeNow());\n }, 1000);\n }\n }\n\n // didMount effect\n (0, _react.useEffect)(function () {\n start();\n return clearIntervalRef;\n }, []);\n\n return _extends({}, _utils.Time.getFormattedTimeFromSeconds(seconds, format));\n}\n\n//# sourceURL=webpack:///./src/useTime.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _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; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nexports.default = useTime;\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _utils = __webpack_require__(/*! ./utils */ \"./src/utils/index.js\");\n\nvar _hooks = __webpack_require__(/*! ./hooks */ \"./src/hooks/index.js\");\n\nfunction useTime(_ref) {\n var format = _ref.format;\n\n var _useState = (0, _react.useState)(_utils.Time.getSecondsFromTimeNow()),\n _useState2 = _slicedToArray(_useState, 2),\n seconds = _useState2[0],\n setSeconds = _useState2[1];\n\n (0, _hooks.useInterval)(function () {\n setSeconds(_utils.Time.getSecondsFromTimeNow());\n }, 1000);\n\n return _extends({}, _utils.Time.getFormattedTimeFromSeconds(seconds, format));\n}\n\n//# sourceURL=webpack:///./src/useTime.js?"); /***/ }), @@ -487,7 +511,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _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; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nexports.default = useTimer;\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _utils = __webpack_require__(/*! ./utils */ \"./src/utils/index.js\");\n\nfunction useTimer(settings) {\n var _ref = settings || {},\n expiry = _ref.expiryTimestamp,\n onExpire = _ref.onExpire;\n\n var _useState = (0, _react.useState)(expiry),\n _useState2 = _slicedToArray(_useState, 2),\n expiryTimestamp = _useState2[0],\n setExpiryTimestamp = _useState2[1];\n\n var _useState3 = (0, _react.useState)(_utils.Time.getSecondsFromExpiry(expiryTimestamp)),\n _useState4 = _slicedToArray(_useState3, 2),\n seconds = _useState4[0],\n setSeconds = _useState4[1];\n\n var _useState5 = (0, _react.useState)(true),\n _useState6 = _slicedToArray(_useState5, 2),\n isRunning = _useState6[0],\n setIsRunning = _useState6[1];\n\n var intervalRef = (0, _react.useRef)();\n\n function clearIntervalRef() {\n if (intervalRef.current) {\n setIsRunning(false);\n clearInterval(intervalRef.current);\n intervalRef.current = undefined;\n }\n }\n\n function handleExpire() {\n clearIntervalRef();\n _utils.Validate.onExpire(onExpire) && onExpire();\n }\n\n function start() {\n if (!intervalRef.current) {\n setIsRunning(true);\n intervalRef.current = setInterval(function () {\n var secondsValue = _utils.Time.getSecondsFromExpiry(expiryTimestamp);\n if (secondsValue <= 0) {\n handleExpire();\n }\n setSeconds(secondsValue);\n }, 1000);\n }\n }\n\n function pause() {\n clearIntervalRef();\n }\n\n function resume() {\n if (!intervalRef.current) {\n setIsRunning(true);\n intervalRef.current = setInterval(function () {\n return setSeconds(function (prevSeconds) {\n var secondsValue = prevSeconds - 1;\n if (secondsValue <= 0) {\n handleExpire();\n }\n return secondsValue;\n });\n }, 1000);\n }\n }\n\n function restart(newExpiryTimestamp) {\n clearIntervalRef();\n setExpiryTimestamp(newExpiryTimestamp);\n }\n\n function handleExtraMilliSeconds(secondsValue, extraMilliSeconds) {\n setIsRunning(true);\n intervalRef.current = setTimeout(function () {\n var currentSeconds = _utils.Time.getSecondsFromExpiry(expiryTimestamp);\n setSeconds(currentSeconds);\n if (currentSeconds <= 0) {\n handleExpire();\n } else {\n intervalRef.current = undefined;\n start();\n }\n }, extraMilliSeconds);\n }\n\n (0, _react.useEffect)(function () {\n if (_utils.Validate.expiryTimestamp(expiryTimestamp)) {\n var secondsValue = _utils.Time.getSecondsFromExpiry(expiryTimestamp);\n var extraMilliSeconds = Math.floor((secondsValue - Math.floor(secondsValue)) * 1000);\n setSeconds(secondsValue);\n if (extraMilliSeconds > 0) {\n handleExtraMilliSeconds(secondsValue, extraMilliSeconds);\n } else {\n start();\n }\n }\n return clearIntervalRef;\n }, [expiryTimestamp]);\n\n return _extends({}, _utils.Time.getTimeFromSeconds(seconds), { start: start, pause: pause, resume: resume, restart: restart, isRunning: isRunning\n });\n}\n\n//# sourceURL=webpack:///./src/useTimer.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _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; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nexports.default = useTimer;\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _utils = __webpack_require__(/*! ./utils */ \"./src/utils/index.js\");\n\nvar _hooks = __webpack_require__(/*! ./hooks */ \"./src/hooks/index.js\");\n\nfunction useTimer(_ref) {\n var expiry = _ref.expiryTimestamp,\n onExpire = _ref.onExpire,\n autoStart = _ref.autoStart;\n\n var _useState = (0, _react.useState)(expiry),\n _useState2 = _slicedToArray(_useState, 2),\n expiryTimestamp = _useState2[0],\n setExpiryTimestamp = _useState2[1];\n\n var _useState3 = (0, _react.useState)(_utils.Time.getSecondsFromExpiry(expiryTimestamp)),\n _useState4 = _slicedToArray(_useState3, 2),\n seconds = _useState4[0],\n setSeconds = _useState4[1];\n\n var _useState5 = (0, _react.useState)(autoStart),\n _useState6 = _slicedToArray(_useState5, 2),\n isRunning = _useState6[0],\n setIsRunning = _useState6[1];\n\n var _useState7 = (0, _react.useState)(autoStart),\n _useState8 = _slicedToArray(_useState7, 2),\n didStart = _useState8[0],\n setDidStart = _useState8[1];\n\n function handleExpire() {\n _utils.Validate.onExpire(onExpire) && onExpire();\n }\n\n function pause() {\n setIsRunning(false);\n }\n\n function resume() {\n var time = new Date();\n time.setSeconds(time.getSeconds() + seconds); // calculate new expiry timestamp based on last paused seconds count\n setExpiryTimestamp(time);\n setIsRunning(true);\n setSeconds(_utils.Time.getSecondsFromExpiry(time));\n }\n\n function start() {\n if (didStart) {\n setIsRunning(true);\n setSeconds(_utils.Time.getSecondsFromExpiry(expiryTimestamp));\n } else {\n resume();\n setDidStart(true);\n }\n }\n\n function restart(newExpiryTimestamp) {\n setDidStart(autoStart);\n setIsRunning(autoStart);\n setExpiryTimestamp(newExpiryTimestamp);\n setSeconds(_utils.Time.getSecondsFromExpiry(newExpiryTimestamp));\n }\n\n (0, _hooks.useInterval)(function () {\n var secondsValue = _utils.Time.getSecondsFromExpiry(expiryTimestamp);\n if (secondsValue <= 0) {\n handleExpire();\n }\n setSeconds(secondsValue);\n }, isRunning ? 1000 : null);\n\n return _extends({}, _utils.Time.getTimeFromSeconds(seconds), { start: start, pause: pause, resume: resume, restart: restart, isRunning: isRunning\n });\n}\n\n//# sourceURL=webpack:///./src/useTimer.js?"); /***/ }), diff --git a/readme.md b/readme.md index f747d2a..ef8b809 100644 --- a/readme.md +++ b/readme.md @@ -33,7 +33,7 @@ function MyTimer({ expiryTimestamp }) { pause, resume, restart, - } = useTimer({ expiryTimestamp, onExpire: () => console.warn('onExpire called') }); + } = useTimer({ expiryTimestamp, autoStart: true, onExpire: () => console.warn('onExpire called') }); return ( @@ -73,8 +73,10 @@ export default function App() { | key | Type | Required | Description | | --- | --- | --- | ---- | | expiryTimestamp | number(timestamp) | YES | this will define for how long the timer will be running | +| autoStart | boolean | No | flag to decide if timer should start automatically | | onExpire | Function | No | callback function to be executed once countdown timer is expired | + ### Values | key | Type | Description | diff --git a/src/hooks/index.js b/src/hooks/index.js new file mode 100644 index 0000000..28e8735 --- /dev/null +++ b/src/hooks/index.js @@ -0,0 +1,5 @@ +import useInterval from './useInterval'; + +export { + useInterval, +}; diff --git a/src/hooks/useInterval.js b/src/hooks/useInterval.js new file mode 100644 index 0000000..7f4603f --- /dev/null +++ b/src/hooks/useInterval.js @@ -0,0 +1,21 @@ +import { useEffect, useRef } from 'react'; + +export default function useInterval(callback, delay) { + const callbacRef = useRef(); + + // update callback function with current render callback that has access to latest props and state + useEffect(() => { + callbacRef.current = callback; + }); + + useEffect(() => { + if (!delay) { + return () => {}; + } + + const interval = setInterval(() => { + callbacRef.current && callbacRef.current(); + }, delay); + return () => clearInterval(interval); + }, [delay]); +} diff --git a/src/useStopwatch.js b/src/useStopwatch.js index a92233c..d5ab05f 100644 --- a/src/useStopwatch.js +++ b/src/useStopwatch.js @@ -1,48 +1,28 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState } from 'react'; import { Time } from './utils'; +import { useInterval } from './hooks'; -export default function useStopwatch(settings) { - const { autoStart, offsetTimestamp } = settings || {}; - +export default function useStopwatch({ autoStart, offsetTimestamp }) { const [seconds, setSeconds] = useState(Time.getSecondsFromExpiry(offsetTimestamp || 0)); const [isRunning, setIsRunning] = useState(autoStart); - const intervalRef = useRef(); - function clearIntervalRef() { - if (intervalRef.current) { - setIsRunning(false); - clearInterval(intervalRef.current); - intervalRef.current = undefined; - } - } + useInterval(() => { + setSeconds((prevSeconds) => (prevSeconds + 1)); + }, isRunning ? 1000 : null); function start() { - if (!intervalRef.current) { - setIsRunning(true); - intervalRef.current = setInterval(() => setSeconds((prevSeconds) => (prevSeconds + 1)), 1000); - } + setIsRunning(true); } function pause() { - clearIntervalRef(); + setIsRunning(false); } function reset(offset: number) { - clearIntervalRef(); + setIsRunning(autoStart); setSeconds(Time.getSecondsFromExpiry(offset || 0)); - if (autoStart) { - start(); - } } - // didMount effect - useEffect(() => { - if (autoStart) { - start(); - } - return clearIntervalRef; - }, []); - return { ...Time.getTimeFromSeconds(seconds), start, pause, reset, isRunning, }; diff --git a/src/useTime.js b/src/useTime.js index ebd47de..106347b 100644 --- a/src/useTime.js +++ b/src/useTime.js @@ -1,31 +1,13 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState } from 'react'; import { Time } from './utils'; +import { useInterval } from './hooks'; -export default function useTime(settings) { - const { format } = settings || {}; - +export default function useTime({ format }) { const [seconds, setSeconds] = useState(Time.getSecondsFromTimeNow()); - const intervalRef = useRef(); - - function clearIntervalRef() { - if (intervalRef.current) { - clearInterval(intervalRef.current); - intervalRef.current = undefined; - } - } - - function start() { - if (!intervalRef.current) { - intervalRef.current = setInterval(() => setSeconds(Time.getSecondsFromTimeNow()), 1000); - } - } - - // didMount effect - useEffect(() => { - start(); - return clearIntervalRef; - }, []); + useInterval(() => { + setSeconds(Time.getSecondsFromTimeNow()); + }, 1000); return { ...Time.getFormattedTimeFromSeconds(seconds, format), diff --git a/src/useTimer.js b/src/useTimer.js index aa6e084..41d21bc 100644 --- a/src/useTimer.js +++ b/src/useTimer.js @@ -1,89 +1,61 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState } from 'react'; import { Time, Validate } from './utils'; +import { useInterval } from './hooks'; -export default function useTimer(settings) { - const { expiryTimestamp: expiry, onExpire } = settings || {}; +const DEFAULT_DELAY = 1000; +export default function useTimer({ expiryTimestamp: expiry, onExpire, autoStart }) { const [expiryTimestamp, setExpiryTimestamp] = useState(expiry); const [seconds, setSeconds] = useState(Time.getSecondsFromExpiry(expiryTimestamp)); - const [isRunning, setIsRunning] = useState(true); - const intervalRef = useRef(); - - function clearIntervalRef() { - if (intervalRef.current) { - setIsRunning(false); - clearInterval(intervalRef.current); - intervalRef.current = undefined; - } - } + const [isRunning, setIsRunning] = useState(autoStart); + const [didStart, setDidStart] = useState(autoStart); + const extraMilliSeconds = Math.floor((seconds - Math.floor(seconds)) * 1000); + const [delay, setDelay] = useState(extraMilliSeconds > 0 ? extraMilliSeconds : 1000); function handleExpire() { - clearIntervalRef(); Validate.onExpire(onExpire) && onExpire(); - } - - function start() { - if (!intervalRef.current) { - setIsRunning(true); - intervalRef.current = setInterval(() => { - const secondsValue = Time.getSecondsFromExpiry(expiryTimestamp); - if (secondsValue <= 0) { - handleExpire(); - } - setSeconds(secondsValue); - }, 1000); - } + setIsRunning(false); + setDelay(null); } function pause() { - clearIntervalRef(); + setIsRunning(false); } - function resume() { - if (!intervalRef.current) { - setIsRunning(true); - intervalRef.current = setInterval(() => setSeconds((prevSeconds) => { - const secondsValue = prevSeconds - 1; - if (secondsValue <= 0) { - handleExpire(); - } - return secondsValue; - }), 1000); - } - } - - function restart(newExpiryTimestamp) { - clearIntervalRef(); + function restart(newExpiryTimestamp, newAutoStart) { + const secondsValue = Time.getSecondsFromExpiry(newExpiryTimestamp); + const extraMilliSecondsValue = Math.floor((secondsValue - Math.floor(secondsValue)) * 1000); + setDelay(extraMilliSecondsValue > 0 ? extraMilliSecondsValue : 1000); + setDidStart(newAutoStart); + setIsRunning(newAutoStart); setExpiryTimestamp(newExpiryTimestamp); + setSeconds(secondsValue); } - function handleExtraMilliSeconds(secondsValue, extraMilliSeconds) { - setIsRunning(true); - intervalRef.current = setTimeout(() => { - const currentSeconds = Time.getSecondsFromExpiry(expiryTimestamp); - setSeconds(currentSeconds); - if (currentSeconds <= 0) { - handleExpire(); - } else { - intervalRef.current = undefined; - start(); - } - }, extraMilliSeconds); + function resume() { + const time = new Date(); + time.setMilliseconds(time.getMilliseconds() + (seconds * 1000)); + restart(time, true); } - useEffect(() => { - if (Validate.expiryTimestamp(expiryTimestamp)) { - const secondsValue = Time.getSecondsFromExpiry(expiryTimestamp); - const extraMilliSeconds = Math.floor((secondsValue - Math.floor(secondsValue)) * 1000); - setSeconds(secondsValue); - if (extraMilliSeconds > 0) { - handleExtraMilliSeconds(secondsValue, extraMilliSeconds); - } else { - start(); - } + function start() { + if (didStart) { + setSeconds(Time.getSecondsFromExpiry(expiryTimestamp)); + setIsRunning(true); + } else { + resume(); } - return clearIntervalRef; - }, [expiryTimestamp]); + } + useInterval(() => { + if (delay !== DEFAULT_DELAY) { + setDelay(DEFAULT_DELAY); + } + const secondsValue = Time.getSecondsFromExpiry(expiryTimestamp); + setSeconds(secondsValue); + if (secondsValue <= 0) { + handleExpire(); + } + }, isRunning ? delay : null); return { ...Time.getTimeFromSeconds(seconds), start, pause, resume, restart, isRunning,