use-saga-reducer
Version:
Use redux-saga without redux
311 lines (261 loc) • 7.7 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = require('react');
var React__default = _interopDefault(React);
var reduxSaga = require('redux-saga');
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(source, true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(source).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
const SagaContext = React.createContext({});
/**
* Passes values into `runSaga` of each decendent `useSagaReducer` call.
*
* Methods are merged with local values, context methods are run first.
* Context value is merged with local value, local values will override
* existing properties.
* @param props Optional saga options
*/
const SagaProvider = (_ref) => {
let {
children
} = _ref,
props = _objectWithoutProperties(_ref, ["children"]);
return React__default.createElement(SagaContext.Provider, {
value: props,
children: children
});
};
function mergeVoidMethods(contextMethod, localMethod) {
if (!contextMethod && !localMethod) {
return;
}
if (contextMethod && !localMethod) {
return contextMethod;
}
if (!contextMethod && localMethod) {
return localMethod;
}
return (...args) => {
contextMethod(...args);
localMethod(...args);
};
}
function mergeSagaMonitors(contextMonitor, localMonitor) {
if (!contextMonitor && !localMonitor) {
return;
}
if (contextMonitor && !localMonitor) {
return contextMonitor;
}
if (!contextMonitor && localMonitor) {
return localMonitor;
}
const sagaMonitorKeys = ['actionDispatched', 'effectCancelled', 'effectRejected', 'effectResolved', 'effectTriggered', 'rootSagaStarted'];
const combinedSagaMonitor = {};
for (const key of sagaMonitorKeys) {
const method = mergeVoidMethods(contextMonitor[key], localMonitor[key]);
if (method) {
combinedSagaMonitor[key] = method;
}
}
return combinedSagaMonitor;
}
function mergeEffectMiddlewares(contextMiddlewares, localMiddlewares) {
if (!contextMiddlewares && !localMiddlewares) {
return;
}
if (contextMiddlewares && !localMiddlewares) {
return contextMiddlewares;
}
if (!contextMiddlewares && localMiddlewares) {
return localMiddlewares;
}
return [...contextMiddlewares, ...localMiddlewares];
}
/**
* Create an saga, disconnected from redux with its own state and dispatch.
*
* @see https://github.com/azmenak/use-saga-reducer
* @example
* ```
* function* dataFetcher() {
* try {
* const data = yield call(API.fetchData)
* yield put({type: 'FETCH_SUCCESS', payload: data})
* } catch (error) {
* yield put({type: 'FETCH_ERROR'})
* }
* }
*
* function* dataFetchingSaga() {
* yield takeLatest('FETCH', dataFetcher)
* }
*
* function reducer(state = {}, action) {
* if (action.type === 'FETCH_SUCCESS') {
* return action.payload
* }
*
* return state
* }
*
* //...
*
* const [state, dispatch] = useSagaReducer(saga, reducer)
* ```
*/
function useSagaReducer(
/**
* Saga method, called when the component mounts, must be a generator function.
* Same as would be passed to reduxSaga.runSaga
*/
saga,
/**
* Reducer method, passed into React's `useReducer` method
*/
reducer,
/**
* Optional initalized argument, passed into React's `useReducer` method
*/
initializerArg,
/**
* Store initialized function, passed into React's `useReducer` method
*/
initializer,
/**
* Additional options passed into the `runSaga` method
*
* Supports:
* ```
* sagaMonitor // each monitor will run context methods then local methods
* onError // runs context method then local method
* context // merges context values into local values
* effectMiddlewares // combines with context middleswares, running context first
* ```
* @see https://redux-saga.js.org/docs/api/#runsagaoptions-saga-args
*/
runSagaOptions) {
const [state, reactDispatch] = React.useReducer(reducer, initializerArg, initializer);
const stateRef = React.useRef(state);
React.useEffect(() => {
stateRef.current = state;
}, [state]);
const sagaIO = React.useMemo(() => {
const channel = reduxSaga.stdChannel();
const dispatch = action => {
reactDispatch(action);
Promise.resolve().then(() => {
channel.put(action);
});
};
const getState = () => stateRef.current;
return {
channel,
dispatch,
getState
};
}, []);
const sagaContext = React.useContext(SagaContext);
React.useEffect(() => {
const options = runSagaOptions || {};
const context = _objectSpread2({}, sagaContext.context, {}, options.context);
const sagaMonitor = mergeSagaMonitors(sagaContext.sagaMonitor, options.sagaMonitor);
const onError = mergeVoidMethods(sagaContext.onError, options.onError);
const effectMiddlewares = mergeEffectMiddlewares(sagaContext.effectMiddlewares, options.effectMiddlewares);
const sagaOptions = _objectSpread2({}, sagaIO, {
context,
sagaMonitor,
onError,
effectMiddlewares
});
const task = reduxSaga.runSaga(sagaOptions, saga);
return () => {
task.cancel();
};
}, []);
return [state, sagaIO.dispatch];
}
/**
* Helper function to create custom redux-saga effects
* @param type unique type string
* @param payload any object
*/
function makeCustomEffect(type, payload) {
return {
'@@redux-saga/custom': true,
combinator: false,
type,
payload
};
}
exports.SagaProvider = SagaProvider;
exports.default = useSagaReducer;
exports.makeCustomEffect = makeCustomEffect;
exports.useSagaReducer = useSagaReducer;
//# sourceMappingURL=index.js.map