redux-polling
v1.1.5
Published
Convenient way to support polling in your Redux app so you can focus on the business logic
Downloads
59
Maintainers
Readme
Redux Polling
Convenient way to support polling in your Redux app so you can focus on the business logic
Almost every app contains charts, progress bars or components that consist on repeated data sampling. This is done by Polling. Support polling in React-Redux application can be frustrating. Each polling scenario requires to write a dedicated reducer and actions, implement the polling (all the setTimeout / setInterval stuff), deal with history and this is even before writing the business logic of polling itself. Redux Polling provides a convenient way to support polling in React-Redux application so you will be able to focus on the business logic right away instead of dealing with all the boilerplate code. The setup is done by adding a middleware and you are ready to go.
You can see a demo here or read the demo source code here.
Installation
npm install --save redux-polling
Or
yarn add redux-polling
Setup
- Add
reduxPollingMiddleware
middleware to your Redux store:
import { createStore, applyMiddleware, compose } from 'redux';
import { reduxPollingMiddleware } from 'redux-polling';
import rootReducer from './path/to/your/root/reducer';
const initialState = {};
const middleware = [
reduxPollingMiddleware,
];
const composedEnhancers = compose(
applyMiddleware(...middleware),
);
const store = createStore(
rootReducer,
initialState,
composedEnhancers,
);
export default store;
- Add
reduxPollingReducer
reducer to your root reducer:
import { combineReducers } from 'redux';
import { reduxPollingNamespace, reduxPollingReducer } from 'redux-polling';
export default combineReducers({
.....
.....
[reduxPollingNamespace]: reduxPollingReducer,
});
- Your application is ready to use Redux Polling everywhere.
Usage example
Let's say we want to implement a chart page that consist on data polling. The page has two components:
- Action panel component that contains two buttons: "Start Polling", "Stop Polling" and "Reset Polling".
- Chart component that shows the data.
Redux Polling provides everything for those components: start and stop actions for the buttons and selectors for receiving the results and history. All we left to do is to implement the polling logic that fetches the data once (Redux Polling will call it on each interval):
import { createPollingActions, getPollingState } from 'redux-polling';
import { createSelector } from 'reselect';
import fetchNextPoint from './path/to/your/points/service';
/* Actions */
const pollingInterval = 1000;
const historyLength = 30;
async function polling() {
const point = await fetchNextPoint();
return point;
}
export const actions = createPollingActions('pointPolling', polling, pollingInterval, historyLength);
/* Selectors */
const isPollingActive = state => getPollingState(state, 'pointPolling').isActive;
const getPointHistory = state => getPollingState(state, 'pointPolling').history;
const getLastPoint = state => getPollingState(state, 'pointPolling').lastEntry;
export const selectors = {
isPollingActive, getPointHistory, getLastPoint,
};
This module exports two items: actions
and selectors
.actions
is an object that contains three action creators: start()
, stop()
and reset()
.selectors
is an object with selectors that our app needs.
Please notice the use of createPollingActions()
and getPollingState()
.
That's all for the polling implementation. Now we can create the components.
- Actions Panel:
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { actions as pointPollingActions } from './state/point-polling';
function ActionsPanel(props) {
const { startPolling, stopPolling } = props;
return (
<div>
<button type="button" onClick={ () => startPolling() }>
Start Polling
</button>
<button type="button" onClick={ () => stopPolling() }>
Stop Polling
</button>
<button type="button" onClick={ () => resetPolling() }>
Reset Polling
</button>
</div>
);
}
ActionsPanel.propTypes = {
startPolling: PropTypes.func,
stopPolling: PropTypes.func,
resetPolling: PropTypes.func,
};
ActionsPanel.defaultProps = {
startPolling: () => {},
stopPolling: () => {},
resetPolling: () => {},
};
const mapDispatchToProps = dispatch => ({
startPolling: () => dispatch(pointPollingActions.start()),
stopPolling: () => dispatch(pointPollingActions.stop()),
resetPolling: () => dispatch(pointPollingActions.reset()),
});
export default connect(
undefined,
mapDispatchToProps,
)(ActionsPanel);
- Chart (I will not get into implementation details of the chart since this is not the topic here):
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import LineChart from 'my-favorite-charts-library';
import { selectors as pointPollingSelectors } from './state/point-polling';
function Chart(props) {
const { isPollingActive, lastPoint, history } = props;
return (
<div>
<div>
{ isPollingActive ? 'Polling Active' : 'Polling Inactive' }
</div>
<div>
{ `Last fetched point is ${lastPoint}` }
</div>
<LineChart data={ history } />
</div>
);
}
Chart.propTypes = {
isPollingActive: PropTypes.bool,
lastPoint: PropTypes.number,
history: PropTypes.arrayOf(PropTypes.number),
};
Chart.defaultProps = {
isPollingActive: false,
lastPoint: undefined,
history: [],
};
const mapStateToProps = state => ({
isPollingActive: pointPollingSelectors.isPollingActive(state),
lastPoint: pointPollingSelectors.getLastPoint(state),
history: pointPollingSelectors.getPointHistory(state),
});
export default connect(
mapStateToProps,
)(Chart);
Cool.
Documentation
redux-polling
module exports several modules:
reduxPollingMiddleware
,reduxPollingReducer
andreduxPollingNamespace
which are required for setup.createPollingActions
andgetPollingState
which are required for polling creation and usage.
reduxPollingMiddleware
reduxPollingMiddleware
is the middleware that should be added to the store. You can see an example here. This middleware is used for intercepting polling actions and operate the polling logic.
reduxPollingReducer
reduxPollingReducer
is the reducer that should be added to the root reducer and reduxPollingNamespace
is the name for that reducer. You can see an example here. This reducer operate the polling state.
createPollingActions(pollingName, callbacks, pollingInterval, historyLimit)
createPollingActions()
is the method that creates for you the polling redux actions. It expects the following arguments:
pollingName
String, required Is a name for this polling operation. You may name your polling operation in any name you like.callbacks
Object or Function, required Is an object that contains callbacks for managing the polling cycle. The most important and the only required callback is the polling function which should do the polling logic. You can find all the available callbacks here. Passing a functionfunc
instead of an object is equal to passing a callbacks object that contains only the polling function:{ polling: func }
.pollingInterval
Number, optional, default is 5000ms Is the polling interval in milliseconds. Each interval starts when polling cycle ends and trigger a new polling cycle.historyLimit
Number, optional, default is 1 Is the number of entries you wish to store for this polling operation. Use0
(zero) to not store any history, and -1 for unlimited history.
createPollingActions()
returns an object with three action creators: start()
, stop()
and reset()
. Dispatching them will start, stop and reset the polling operation.
Important: Any argument that will be supplied to start()
will be passed to the polling callback:
async function polling(accountId) {
const point = await fetchNextPoint(accountId);
return point;
}
const actions = createPollingActions('pointPolling', { polling }, 1000, 30);
...
...
const accountId = 428;
dispatch(actions.start(accountId));
...
...
dispatch(actions.stop());
...
...
// When required to reset the polling state
dispatch(actions.reset());
In this example polling started with account id as an argument (actions.start(accountId);
). When the polling(accountId)
method will be called, the accountId
argument that was provided to start()
will also be provided to polling()
. There is no limitation on the arguments start()
can get. They simply will be passed to polling()
.
getPollingState(state, pollingName)
getPollingState(pollingName)
is a helper selector to retrieve the state of the polling operation named by pollingName
argument. Like any other selector it gets the global state (state
) and the name of the required polling operation state (pollingName
). The returned polling state is an object that looks like:
{
isActive: false, // Indicates whther the polling is activated or not
history: [], // History entries
lastEntry: undefined, // The last fetched entry
}
Available Callbacks
The second argument createPollingActions()
expected is an object with callbacks. Those callbacks are called during a polling cycle. The available callbacks are:
polling(...args, getState)
required Each interval starts with calling to thepolling
callback that should do a single fetch / calculation and return a single entry....args
are the arguments that was provided to thestart(args)
action.getState()
method is also provided as the last argument so you can query your state. This method can be asynchronous and can return a promise. The returned / resolved value is store as an entry in the polling history.
Sometimes you may want to return and store multiple items in the history for single polling. You can do that by returning the following{ multipleEntries: true, entries: [ ...entries ] }
.
Example:
async function polling(accountId, getState) { // accountId was provided by start() action
const state = getState();
const pointsCount = selectors.getPointsCount();
const points = await fetchMultiplePoints(accountId, pointsCount);
// points is an array
return { multipleEntries: true, entries: points};
}
shouldAddEntry(entry, getState)
optional This callback is called right before adding an entry to the state. It gets a single entry (which is the response of thepolling
callback) andgetState()
method. It should return a boolean that indicates whether to add the entry to the state.
Release History
- 1.1.1
- Support returning multiple entries in the polling callback
- Support reset action to reset the state
- Remove initialPolling callback (can be handled in the polling callback by the user)
- 1.0.0
- First stable version
Meta
Naor Ye – [email protected]
Distributed under the MIT license. See LICENSE
for more information.
https://github.com/naorye/redux-polling
Contributing
- Fork it (https://github.com/naorye/redux-polling/fork)
- Create your feature branch (
git checkout -b feature/fooBar
) - Commit your changes (
git commit -am 'Add some fooBar'
) - Push to the branch (
git push origin feature/fooBar
) - Create a new Pull Request