sagaInjectors.ts 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import invariant from 'invariant'
  2. import isEmpty from 'lodash/isEmpty'
  3. import isFunction from 'lodash/isFunction'
  4. import isString from 'lodash/isString'
  5. import conformsTo from 'lodash/conformsTo'
  6. import checkStore from './checkStore'
  7. import { DAEMON, ONCE_TILL_UNMOUNT, RESTART_ON_REMOUNT } from './constants'
  8. const allowedModes = [RESTART_ON_REMOUNT, DAEMON, ONCE_TILL_UNMOUNT]
  9. const checkKey = (key) =>
  10. invariant(
  11. isString(key) && !isEmpty(key),
  12. '(app/utils...) injectSaga: Expected `key` to be a non empty string'
  13. )
  14. const checkDescriptor = (descriptor) => {
  15. const shape = {
  16. saga: isFunction,
  17. mode: (mode) => isString(mode) && allowedModes.includes(mode)
  18. }
  19. invariant(
  20. conformsTo(descriptor, shape),
  21. '(app/utils...) injectSaga: Expected a valid saga descriptor'
  22. )
  23. }
  24. export function injectSagaFactory (store, isValid) {
  25. return function injectSaga (key, descriptor: { saga?: any, mode?: string } = {}, args?: any) {
  26. if (!isValid) { checkStore(store) }
  27. const newDescriptor = {
  28. ...descriptor,
  29. mode: descriptor.mode || DAEMON
  30. }
  31. const { saga, mode } = newDescriptor
  32. checkKey(key)
  33. checkDescriptor(newDescriptor)
  34. let hasSaga = Reflect.has(store.injectedSagas, key)
  35. if (process.env.NODE_ENV !== 'production') {
  36. const oldDescriptor = store.injectedSagas[key]
  37. // enable hot reloading of daemon and once-till-unmount sagas
  38. if (hasSaga && oldDescriptor.saga !== saga) {
  39. oldDescriptor.task.cancel()
  40. hasSaga = false
  41. }
  42. }
  43. if (
  44. !hasSaga ||
  45. (hasSaga && mode !== DAEMON && mode !== ONCE_TILL_UNMOUNT)
  46. ) {
  47. /* eslint-disable no-param-reassign */
  48. store.injectedSagas[key] = {
  49. ...newDescriptor,
  50. task: store.runSaga(saga, args)
  51. }
  52. /* eslint-enable no-param-reassign */
  53. }
  54. }
  55. }
  56. export function ejectSagaFactory (store, isValid) {
  57. return function ejectSaga (key) {
  58. if (!isValid) { checkStore(store) }
  59. checkKey(key)
  60. if (Reflect.has(store.injectedSagas, key)) {
  61. const descriptor = store.injectedSagas[key]
  62. if (descriptor.mode && descriptor.mode !== DAEMON) {
  63. descriptor.task.cancel()
  64. // Clean up in production in development we need `descriptor.saga` for hot reloading
  65. if (process.env.NODE_ENV === 'production') {
  66. // Need some value to be able to detect `ONCE_TILL_UNMOUNT` sagas in `injectSaga`
  67. store.injectedSagas[key] = 'done' // eslint-disable-line no-param-reassign
  68. }
  69. }
  70. }
  71. }
  72. }
  73. export default function getInjectors (store) {
  74. checkStore(store)
  75. return {
  76. injectSaga: injectSagaFactory(store, true),
  77. ejectSaga: ejectSagaFactory(store, true)
  78. }
  79. }