redux-saga-modal v1.3.2
redux-saga-modal
Manage modals with redux-saga.
Live Demo
Install
npm i redux-saga-modalOr
yarn add redux-saga-modalUsage
Pass the reducer to your store. It keeps the state of all your modal
components, so you only have to pass it once.
import { createStore, combineReducers } from 'redux';
import { reducer as modalReducer } from 'redux-saga-modal';
const rootReducer = combineReducers({
// ...your other reducers
// modalReducer should be mounted under 'modals' key,
modals: modalReducer,
});
const store = createStore(rootReducer);Connect a component to Redux Store with sagaModal.
import {
sagaModal,
createModal,
SagaModalInjectedProps,
} from 'redux-saga-modal';
import ReactModal from 'react-modal';
import { ReactNode } from 'react';
ReactModal.setAppElement('#root');
export interface ConfirmModalOwnProps {
title: ReactNode;
text: ReactNode;
}
const Confirm: React.FC<ConfirmModalOwnProps & SagaModalInjectedProps> = ({
isOpen,
submit,
hide,
title,
text,
}) => {
const handleSubmit = () => {
submit();
};
return (
<ReactModal isOpen={isOpen} onRequestClose={hide} closeTimeoutMS={500}>
<h3>{title}</h3>
<p>{text}</p>
<div>
<button onClick={hide}>Cancel</button>
<button onClick={handleSubmit}>Confirm</button>
</div>
</ReactModal>
);
};
const modal = createModal('confirm');
const ConnectedConfirmModal = sagaModal({
name: modal.name,
})(Confirm);
export { modal, ConnectedConfirmModal };To create an instance use createModal and pass the modal name as the
first argument.
import { createModal } from 'redux-saga-modal';
const modal = createModal('CONFIRM');Result
{
name: 'CONFIRM',
selector: function (r) {},
patterns: {
show: function (n) {},
hide: function (n) {},
destroy: function (n) {},
update: function (n) {},
click: function (n) {},
submit: function (n) {},
},
actions: {
show: function (n) {},
update: function (n) {},
submit: function (n) {},
click: function (n) {},
hide: function () {},
destroy: function () {},
},
takeShow: function (t) {},
takeUpdate: function (t) {},
takeClick: function (t) {},
takeDestroy: function (t) {},
takeSubmit: function (t) {},
takeHide: function (t) {},
show: function (r) {},
update: function (r) {},
click: function (r) {},
submit: function (r) {},
hide: function () {},
destroy: function () {},
select: function () {},
};All methods already bound to the modal's name so you don't need manually
pass it. The instance will receive properties actions,
patterns, name, selector and high-level
effects show, update, hide, submit, click,
destroy, takeShow, takeUpdate, takeHide, takeSubmit,
takeClick and takeDestroy.
Both patterns and actions have methods named show, update,
hide, submit, click and destroy.
import { race } from 'redux-saga/effects';
import { createModal } from 'redux-saga-modal';
const modal = createModal('confirm');
const confirmModal = function* (initProps: ConfirmModalOwnProps) {
yield modal.show(initProps);
const [submit]: boolean[] = yield race([
modal.takeSubmit(),
modal.takeHide(),
]);
yield modal.hide();
return !!submit;
};Alternatively if you don't need high-level effects you can use createModalHelpers. The example above can be rewritten like this:
import { race, put, take } from 'redux-saga/effects';
import { createModalHelpers } from 'redux-saga-modal';
import { ConfirmModalOwnProps } from './Modal';
const modal = createModalHelpers('confirm');
const confirmModal = function* (initProps: ConfirmModalOwnProps) {
yield put(modal.actions.show(initProps));
const [submit]: boolean[] = yield race([
take(modal.patterns.submit()),
take(modal.patterns.hide()),
]);
yield put(modal.actions.hide());
return !!submit;
};For frequently repeated modals tasks create a config object with modals
names as keys and tasks as values. Pass the config as the argument to
sagas and fork it in your rootSaga.
Your tasks will be called with a this context every time when an
action showModal has been dispatched with it's name and cancelled
on destroyModal.
The context of your task will have properties actions, patterns,
name, selector and high-level effects show, update,
hide, submit, click, destroy, takeShow, takeUpdate,
takeHide, takeSubmit, takeClick and takeDestroy.
Both patterns and actions have methods named show, update,
hide, submit, click and destroy.
import { race, call, fork, all } from 'redux-saga/effects';
import {
sagas as modalsSaga,
SagaModalInstance,
SagaModalAction,
} from 'redux-saga-modal';
interface CommentData {
id: number;
comment: string;
}
const saveComment = (data: CommentData) =>
new Promise((resolve) => setTimeout(() => resolve({ ok: true }), 1000));
const saveCommentWorker = function* (this: SagaModalInstance) {
const [submitAction]: SagaModalAction<CommentData>[] = yield race([
this.takeSubmit(),
this.takeHide(),
]);
if (submitAction) {
yield this.update({
title: 'Saving',
loading: true,
});
yield call(saveComment, submitAction.payload as CommentData);
yield this.update({
title: 'Changes saved',
loading: false,
});
}
yield this.hide();
};
const modalsTasks = {
saveComment: saveCommentWorker,
};
export default function* rootSaga() {
yield all([fork(modalsSaga, modalsTasks)]);
}API
Actions Creators
Basic actions creators.
| Name | Arguments | Description |
|---|---|---|
| showModal | (name: String, payload: Object = {}) | Triggers to show a modal and sets payload as props |
| updateModal | (name: String, payload: Object = {}) | Updates the modal by merging payload with it's current props |
| submitModal | (name: String, payload: any) | Triggers that a target's button was pressed |
| clickModal | (name: String, payload: any) | A trigger for handling any click on modal |
| hideModal | (name: String) | Hide modal without destroying its state in the redux store |
| destroyModal | (name: String) | Close a modal by destroying its props |
Instance Creators
createModal
Creates a modal instance with properties name, selector, actions,
patterns, and effects creators show, update, hide,
submit, click, destroy, takeShow, takeUpdate, takeHide,
takeSubmit, takeClick and takeDestroy.
Both patterns and actions includes methods named show, update,
hide, submit, click and destroy. All methods refer to the
modal's name so you don't need manually pass it.
Arguments
name(String)(Required) the name of a modal
config(Object)
- getModalsState (Function) A selector that takes the Redux
store and returns the slice which corresponds to where the
redux-saga-modal
reducerwas mounted. By default, the reducer is mounted under themodalskey.
- getModalsState (Function) A selector that takes the Redux
store and returns the slice which corresponds to where the
redux-saga-modal
import { createModal } from 'redux-saga-modal';
const {
name,
selector,
//patterns creators used for filtering actions inside take effects
patterns: { show, click, submit, update, hide, destroy },
//action creators
actions: { show, click, submit, update, hide, destroy },
//put effects (already wrapped in redux-saga put)
show,
click,
submit,
update,
hide,
destroy,
//take effects (already wrapped in redux-saga take)
takeShow,
takeClick,
takeSubmit,
takeUpdate,
takeHide,
takeDestroy,
} = createModal('CONFIRM');createModalHelpers
Creates a modal instance with properties
actions, patterns, selector and name.
Both patterns and actions have methods named show, update,
hide, submit, click and destroy.
Arguments
name(String)(Required) the name of a modal
config(Object)
- getModalsState (Function) A selector that takes the Redux store
and returns the slice which corresponds to where the
redux-saga-modal
reducerwas mounted. By default, the reducer is mounted under themodalskey.
- getModalsState (Function) A selector that takes the Redux store
and returns the slice which corresponds to where the
redux-saga-modal
import { createModalHelpers } from 'redux-saga-modal';
const {
name,
selector,
patterns: { show, click, submit, update, hide, destroy },
actions: { show, click, submit, update, hide, destroy },
} = createModalHelpers('CONFIRM');Instance properties
name
The name of the modal instance which was passed to createModal or
createModalHelpers. Should be the same as passed one in sagaModal.
actions
Same as basic actions creators but the first argument is already bound to the modal name.
| Name | Arguments | Description |
|---|---|---|
| show | (payload: Object = {}) | Triggers to show a modal and sets payload as it's props |
| update | (payload: Object = {}) | Updates the modal by merging payload with it's current props |
| submit | (payload: any) | Triggers that a target's button was pressed |
| click | (payload: any) | A trigger for handling any click on modal |
| hide | () | Hide modal without destroying its state in the redux store |
| destroy | () | Close a modal by destroying its props |
Example
import { createModal, createModalActions, showModal } from 'redux-saga-modal';
const modal = createModal('CONFIRM');
yield put(modal.actions.show({ title: 'Some title' }));
//or
const confirmActions = createModalActions('CONFIRM');
yield put(confirmActions.show({ title: 'Some title' }));
//or
yield put(showModal('CONFIRM', { title: 'Some title' }));patterns
Matcher methods for filtering modal actions inside redux-saga take
effects, such as take, takeLatest, takeEvery etc. Every pattern
accepts optional argument for checking payload.
Payload pattern has the
same meaning and
rules as in the redux-saga but applies not for the whole action but
only to its payload.
| Name | Arguments | Description |
|---|---|---|
| show | payloadPattern?: String | Function |
| update | payloadPattern?: String | Function |
| submit | payloadPattern?: String | Function |
| click | payloadPattern?: String | Function |
| hide | An action matcher for hideModal with the same name as it's instance has | |
| destroy | An action matcher for destroyModal with the same name as it's instance has |
Example
import { createModal } from 'redux-saga-modal';
const { patterns } = createModal('CONFIRM');
//result: take(action => action.type === clickModal().type && action.meta.name === 'CONFIRM' && payload === 'value'
yield take(patterns.click(payload === 'value'));
//result: take(action => action.type === clickModal().type && action.meta.name === 'CONFIRM' && payload && payload.text === 'Some text'
yield take(patterns.show((payload) => payload && payload.text === 'Some text'));
//result: take(action => action.type === clickModal().type && action.meta.name === 'CONFIRM'
yield take(patterns.click);selector
import { createModal } from 'redux-saga-modal';
const { selector, ...effects } = createModal('CONFIRM');
// the same
yield select(selector);
yield effects.select();effects
Produced with createModal or createModalEffects. Returns scoped
put, select and take effects.
Payload pattern in the take effects has the same meaning and rules as
in's the redux-saga, but applies not for the whole action but only to
the payload.
| Name | Arguments | Effect | |
|---|---|---|---|
| show | (payload: Object) | put | Triggers to show a modal and sets payload as it's props |
| update | (payload: Object) | put | Updates the modal by merging payload with it's current props |
| submit | (payload: any) | put | Triggers that a target's button was pressed |
| click | (payload: any) | put | A trigger for handling any click on modal |
| hide | (name: String) | put | Hide modal without destroying it's state in the redux store |
| destroy | (name: String) | put | Close a modal by destroying it's props |
| select | (name: String) | select | Select a modal state from the Redux Store |
| takeShow | (payloadPattern?: String | Function | Array |
| takeUpdate | (payloadPattern?: String | Function | Array |
| takeSubmit | (payloadPattern?: String | Function | Array |
| takeClick | (payloadPattern?: String | Function | Array |
| takeHide | take | Returns a take effect for an action hideModal with scoped name | |
| takeDestroy | take | Returns a take effect for an action destroyModal with scoped name |
Example
import { createModal } from 'redux-saga-modal';
const { name, ...effects } = createModal('CONFIRM');
//result: put(showModal('CONFIRM', ({ text: 'Some text' }))
yield effects.show({ text: 'Some text' });
//result: take(action => action.type === clickModal().type && action.meta.name === 'CONFIRM' && payload === 'value'
yield effects.takeClick('value');
//result: take(action => action.type === clickModal().type && action.meta.name === 'CONFIRM' && payload && payload.text === 'Some text'
yield effects.takeClick((payload) => payload && payload.text === 'Some text');
//result: take(action => action.type === clickModal().type && action.meta.name === 'CONFIRM'
yield effects.takeClick();sagas
Invoke your sagas with a this context whenever an action showModal
has been dispatched with passed name and cancelled on
destroyModal.
Arguments
config(Object)(Required)
- \<[key: ModalName], Saga: Generator>
sagaModal
Connects a component to Redux store and injects next props:
- modal: { name }
- name;
- isOpen;
- show;
- update;
- destroy;
- click;
- submit;
- hide;
- showModal;
- updateModal;
- submitModal;
- clickModal;
- hideModal;
- destroyModal;
Arguments
name(String)(Required) the name of a modal
config(Object)
- name (String)(Required) the name of a modal
- getModalsState (Function) A selector that takes the Redux
store and returns the slice which corresponds to where the
redux-saga-modal
reducerwas mounted. By default reducer is mounted under the 'modals' key:state => state.modals - actions (Object) Custom actions to bind with redux
dispatch - keepComponentOnHide (Boolean) Whether keep modal component
when
isOpenequalsfalseBy default equalsfalse - destroyOnHide (Boolean) Whether automatically dispatch
destroyto afterhide. By default equalstrue
reducer
The modals reducer keeps state of the all modals. Should be mounted to
redux store under name modals or any other but in this case you need
to pass selector getModalsState to sagaModal, createModal and
createModalHelpers.
createModalActions
Arguments
- name(String)(Required) the name of a modal
Creates actions creators show, update, hide, submit,
click and destroy bound to the name of the modal.
createModalEffects
Arguments
- name (String)(Required) the name of a modal
Creates effects show, update, hide, submit, click
and destroy bound to the name of the modal.
createModalPatterns
Arguments
- name(String)(Required) the name of a modal
Creates patterns creators show, update, hide,
submit, click and destroy bound to the name of modal. Every
pattern accepts optional matcher for checking payload.
License
MIT © KarimAziev
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago