Skip to content

Instantly share code, notes, and snippets.

@afitiskin
Last active March 11, 2021 04:14
Show Gist options
  • Save afitiskin/575e1f23f8ff6f8ca5f4 to your computer and use it in GitHub Desktop.
Save afitiskin/575e1f23f8ff6f8ca5f4 to your computer and use it in GitHub Desktop.

Redux-based flux implementation with promises support

There is middleware.js that extends basic dispatcher to support promises. Each action with promise will dispatch 2 simple actions: one when request started (to show preloader) and another when request finished (with success or error). action-creator.js is a simple example of how promise-actions is looks like: it is a plain object with 2 keys: promise and types. store.js is a simple example of store (reducer) that contain both data and loading statuses for some data object. It listen 3 types of actions and changes data accordingly. All works great until we try to implement growl-like notifications for each action.

Notifications

It is a simple store, that listen all success / error actions and push new notification into collection of notifications. It also support feature to hide notification (remove item by id from collection). I would like to have notifications, that display only for a few seconds and then will dissapear. So, redux way is to dispatch some action by timeout, that will remove notification from list. Main question is: from where and how to dispatch these kind of actions and how to connect these actions with specific notifications?

import { LOAD_SOMETHING, LOAD_SOMETHING_SUCCESS, LOAD_SOMETHING_FAIL } from './action-types';
export function loadSomething() {
return {
types: [LOAD_SOMETHING, LOAD_SOMETHING_SUCCESS, LOAD_SOMETHING_FAIL],
promise: loadFromServer('/api/something'),
};
}
export default function promiseMiddleware(client, getState) {
return function (next) {
const recurse = function (action) {
const { promise, types, ...rest } = action;
if (!promise) {
// if there is no promise in action – make default dispatch
return typeof action === 'function' ? action(recurse, getState) : next(action);
}
// if promise exists
const [REQUEST, SUCCESS, FAILURE] = types;
// first, dispatch request action (request initialized)
next({...rest, type: REQUEST});
return promise(client).then(
// then depends on result
// dispatch request success action
(result) => next({...rest, result, type: SUCCESS}),
// or request error action
(error) => next({...rest, error, type: FAILURE})
);
};
return recurse;
};
}
import { LOAD_SOMETHING_SUCCESS, LOAD_SOMETHING_FAIL, HIDE_NOTIFICATION_BY_ID, HIDE_NOTIFICATION_BY_TIMEOUT } from './action-types';
let id = 0;
const initialState = [];
export default function notification(state = initialState, action = {}) {
switch (action.type) {
case LOAD_SOMETHING_SUCCESS:
// add new success notification
return state.concat({
id: ++id,
type: 'success',
message: 'Something successfully loaded!'
});
case LOAD_SOMETHING_FAIL:
// add new error notification
return state.concat({
id: ++id,
type: 'error',
message: 'Could not load something! Please try again later!'
});
case HIDE_NOTIFICATION_BY_ID:
// hide notification by id, e.g. when user click on it
return state.filter((item) => item.id != action.notificationId);
case HIDE_NOTIFICATION_BY_TIMEOUT:
// ??? when and how it have to be dispatched ???
return state;
default:
return state;
}
}
import { LOAD_SOMETHING, LOAD_SOMETHING_SUCCESS, LOAD_SOMETHING_FAIL } from './action-types';
const initialState = {
loading: true,
loaded: false,
error: false,
data: []
};
export default function something(state = initialState, action = {}) {
switch (action.type) {
case LOAD_SOMETHING:
return {
...state,
loading: true,
error: false
};
case LOAD_SOMETHING_SUCCESS:
return {
...state,
loading: false,
loaded: true,
data: action.result
};
case LOAD_SOMETHING_FAIL:
return {
...state,
loading: false,
loaded: false,
error: action.error
};
default:
return state;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment