1.1.5 • Published 6 years ago

redux-polling v1.1.5

Weekly downloads
34
License
MIT
Repository
github
Last release
6 years ago

Redux Polling

Convenient way to support polling in your Redux app so you can focus on the business logic

NPM Version

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

  1. 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;
  1. Add reduxPollingReducer reducer to your root reducer:
import { combineReducers } from 'redux';
import { reduxPollingNamespace, reduxPollingReducer } from 'redux-polling';

export default combineReducers({
    .....
    .....
    [reduxPollingNamespace]: reduxPollingReducer,
});
  1. 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: 1. Action panel component that contains two buttons: "Start Polling", "Stop Polling" and "Reset Polling". 2. 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. 1. 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);
  1. 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 and reduxPollingNamespace which are required for setup.
  • createPollingActions and getPollingState 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 function func 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. Use 0 (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 the polling callback that should do a single fetch / calculation and return a single entry. ...args are the arguments that was provided to the start(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 the polling callback) and getState() 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 – naorye@gmail.com

Distributed under the MIT license. See LICENSE for more information.

https://github.com/naorye/redux-polling

Contributing

  1. Fork it (https://github.com/naorye/redux-polling/fork)
  2. Create your feature branch (git checkout -b feature/fooBar)
  3. Commit your changes (git commit -am 'Add some fooBar')
  4. Push to the branch (git push origin feature/fooBar)
  5. Create a new Pull Request
1.1.5

6 years ago

1.1.2

6 years ago

1.1.1

6 years ago

1.0.4

6 years ago

1.0.3

6 years ago

1.0.0

6 years ago

0.0.1-beta9

6 years ago

0.0.1-beta8

6 years ago

0.0.1-beta7

6 years ago

0.0.1-beta6

6 years ago

0.0.1-beta5

6 years ago

0.0.1-beta4

6 years ago

0.0.1-beta3

6 years ago

0.0.1-beta2

6 years ago

0.0.1-beta1

6 years ago

0.0.1-beta

6 years ago