├── .gitattributes
├── example
├── src
│ ├── server.js
│ ├── redux
│ │ ├── middleware
│ │ │ └── index.js
│ │ ├── reducers
│ │ │ ├── index.js
│ │ │ └── example.js
│ │ └── index.js
│ ├── components
│ │ ├── Spacer.jsx
│ │ ├── Title.jsx
│ │ ├── Output.jsx
│ │ └── Input.jsx
│ ├── client.js
│ ├── containers
│ │ └── AppContainer.jsx
│ └── server
│ │ └── index.js
├── test
│ ├── redux
│ │ ├── reducers
│ │ │ ├── index.js
│ │ │ └── example.js
│ │ └── index.js
│ ├── components
│ │ ├── Spacer.js
│ │ ├── Title.js
│ │ ├── Output.js
│ │ └── Input.js
│ └── containers
│ │ └── AppContainer.js
├── webpack.config.js
└── package.json
├── .travis.yml
├── .gitignore
├── .editorconfig
├── src
└── index.js
├── license
├── package.json
├── readme.md
└── test
└── index.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.js text eol=lf
3 |
--------------------------------------------------------------------------------
/example/src/server.js:
--------------------------------------------------------------------------------
1 | require('babel-register')
2 | require('./server/')
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '6'
4 | - '4'
5 | after_success:
6 | npm run coverage
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Directories
2 | .nyc_output
3 | coverage
4 | lib
5 | node_modules
6 |
7 | # Files
8 | .DS_Store
9 | npm-debug.log
10 |
--------------------------------------------------------------------------------
/example/src/redux/middleware/index.js:
--------------------------------------------------------------------------------
1 | import debounce from '../../../../src'
2 |
3 | export default () => ([
4 | debounce({ simple: 500 }),
5 | ])
6 |
--------------------------------------------------------------------------------
/example/src/components/Spacer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Spacer = () => (
4 |
5 | )
6 |
7 | export default Spacer
8 |
--------------------------------------------------------------------------------
/example/src/redux/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import example from './example'
3 |
4 | export default combineReducers({
5 | example,
6 | })
7 |
--------------------------------------------------------------------------------
/example/src/components/Title.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Title = () => (
4 |
5 |
redux-debounce demo
6 |
7 |
8 | )
9 |
10 | export default Title
11 |
--------------------------------------------------------------------------------
/example/test/redux/reducers/index.js:
--------------------------------------------------------------------------------
1 | import reducers from '../../../src/redux/reducers'
2 | import test from 'ava'
3 |
4 | test('combines reducers', t => {
5 | const reducer = reducers()
6 |
7 | t.is(reducer.example, '')
8 | })
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | charset = utf-8
3 | end_of_line = lf
4 | indent_size = 2
5 | indent_style = space
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 |
9 | [*.md]
10 | trim_trailing_whitespace = false
11 |
--------------------------------------------------------------------------------
/example/src/redux/index.js:
--------------------------------------------------------------------------------
1 | import { applyMiddleware, createStore } from 'redux'
2 | import createMiddleware from './middleware'
3 | import reducers from './reducers'
4 |
5 | export default () =>
6 | applyMiddleware(...createMiddleware())(createStore)(reducers)
7 |
--------------------------------------------------------------------------------
/example/src/components/Output.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 |
3 | const Output = ({ value }) => (
4 |
5 |
6 | {value}
7 |
8 | )
9 |
10 | Output.propTypes = {
11 | value: PropTypes.string,
12 | }
13 |
14 | export default Output
15 |
--------------------------------------------------------------------------------
/example/test/components/Spacer.js:
--------------------------------------------------------------------------------
1 | import { shallow } from 'enzyme'
2 | import React from 'react'
3 | import Spacer from '../../src/components/Spacer.jsx'
4 | import test from 'ava'
5 |
6 | test('renders', t => {
7 | const component = shallow()
8 |
9 | t.deepEqual(component.prop('style'), { height: '1em' })
10 | })
11 |
--------------------------------------------------------------------------------
/example/test/redux/index.js:
--------------------------------------------------------------------------------
1 | import createStore from '../../src/redux'
2 | import test from 'ava'
3 |
4 | test('creates a store', t => {
5 | const { dispatch, subscribe, getState } = createStore()
6 |
7 | t.is(typeof dispatch, 'function')
8 | t.is(typeof subscribe, 'function')
9 | t.is(typeof getState, 'function')
10 | })
11 |
--------------------------------------------------------------------------------
/example/test/components/Title.js:
--------------------------------------------------------------------------------
1 | import { shallow } from 'enzyme'
2 | import React from 'react'
3 | import Title from '../../src/components/Title.jsx'
4 | import test from 'ava'
5 |
6 | test('renders a value', t => {
7 | const component = shallow()
8 |
9 | t.is(component.find('h1').text(), 'redux-debounce demo')
10 | })
11 |
--------------------------------------------------------------------------------
/example/test/components/Output.js:
--------------------------------------------------------------------------------
1 | import { shallow } from 'enzyme'
2 | import Output from '../../src/components/Output.jsx'
3 | import React from 'react'
4 | import test from 'ava'
5 |
6 | test('renders a value', async t => {
7 | const component = shallow()
8 |
9 | t.is(component.find('span').text(), 'Hi!')
10 | })
11 |
--------------------------------------------------------------------------------
/example/src/client.js:
--------------------------------------------------------------------------------
1 | import { Provider } from 'react-redux'
2 | import { render } from 'react-dom'
3 | import AppContainer from './containers/AppContainer'
4 | import React from 'react'
5 | import createStore from './redux'
6 |
7 | const store = createStore()
8 |
9 | render((
10 |
11 |
12 |
13 | ), document.getElementById('root'))
14 |
--------------------------------------------------------------------------------
/example/src/components/Input.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 |
3 | const Input = ({ onChange, value }) => (
4 |
5 |
6 |
10 |
11 | )
12 |
13 | Input.propTypes = {
14 | onChange: PropTypes.func,
15 | value: PropTypes.string,
16 | }
17 |
18 | export default Input
19 |
--------------------------------------------------------------------------------
/example/src/redux/reducers/example.js:
--------------------------------------------------------------------------------
1 | const INPUT = 'redux-debounce-example/example/INPUT'
2 |
3 | const initialState = ''
4 |
5 | const reducer = ( state = initialState, action = {} ) => {
6 | switch ( action.type ) {
7 | case INPUT:
8 | return action.payload
9 | default:
10 | return state
11 | }
12 | }
13 |
14 | export const input = payload => ({
15 | meta: {
16 | debounce: 'simple',
17 | },
18 | payload,
19 | type: INPUT,
20 | })
21 |
22 | export default reducer
23 |
--------------------------------------------------------------------------------
/example/webpack.config.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 |
3 | export default {
4 | devtool: 'eval',
5 | entry: './src/client',
6 | output: {
7 | path: path.join(__dirname, 'dist'),
8 | filename: 'bundle.js',
9 | publicPath: '/static/',
10 | },
11 | module: {
12 | loaders: [
13 | {
14 | test: /\.jsx?$/,
15 | loader: 'babel',
16 | exclude: /node_modules/,
17 | },
18 | ],
19 | },
20 | resolve: {
21 | extensions: [ '', '.js', '.jsx' ],
22 | },
23 | }
24 |
--------------------------------------------------------------------------------
/example/test/redux/reducers/example.js:
--------------------------------------------------------------------------------
1 | import reducer, { input } from '../../../src/redux/reducers/example'
2 | import test from 'ava'
3 |
4 | test('reduces', t => {
5 | const state = reducer()
6 |
7 | t.is(state, '')
8 |
9 | const action = input('something')
10 | const newState = reducer(state, action)
11 |
12 | t.is(newState, 'something')
13 | })
14 |
15 | test('has input action', t => {
16 | const action = input('text')
17 |
18 | t.is(action.payload, 'text')
19 | t.deepEqual(action.meta, { debounce: 'simple' })
20 | t.truthy(action.type)
21 | })
22 |
--------------------------------------------------------------------------------
/example/test/components/Input.js:
--------------------------------------------------------------------------------
1 | import { shallow } from 'enzyme'
2 | import { spy } from 'sinon'
3 | import Input from '../../src/components/Input.jsx'
4 | import React from 'react'
5 | import test from 'ava'
6 |
7 | test('renders a value', t => {
8 | const component = shallow()
9 |
10 | t.is(component.find('input').prop('defaultValue'), 'Hi!')
11 | })
12 |
13 | test('fires onChange', t => {
14 | const onChange = spy()
15 | const component = shallow()
16 |
17 | component.find('input').simulate('change', 'Hello!')
18 |
19 | t.truthy(onChange.called)
20 | t.truthy(onChange.calledWith('Hello!'))
21 | })
22 |
--------------------------------------------------------------------------------
/example/test/containers/AppContainer.js:
--------------------------------------------------------------------------------
1 | import {
2 | AppContainer,
3 | mapStateToProps,
4 | } from '../../src/containers/AppContainer.jsx'
5 | import { shallow } from 'enzyme'
6 | import React from 'react'
7 | import test from 'ava'
8 |
9 | test('renders', t => {
10 | const component = shallow()
11 |
12 | t.truthy(component.find('Title').length)
13 | t.truthy(component.find('Spacer').length)
14 | t.truthy(component.find('Input').length)
15 | t.truthy(component.find('Output').length)
16 | t.is(component.find('Output').prop('value'), 'Hi!')
17 | })
18 |
19 | test('maps state to props', t => {
20 | const props = mapStateToProps({ example: 'Something.' })
21 |
22 | t.deepEqual(props, { value: 'Something.' })
23 | })
24 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { isFSA } from 'flux-standard-action'
2 | import debounce from 'lodash.debounce'
3 | import mapValues from 'lodash.mapvalues'
4 |
5 | const debounceMiddleware = ( config = {} ) => () => next => {
6 | const debouncers = mapValues(config, option => {
7 | if ( typeof option === 'number' ) {
8 | return debounce(next, option)
9 | }
10 |
11 | const { wait = 0, ...options } = option
12 |
13 | return debounce(next, wait, options)
14 | })
15 |
16 | return action => {
17 | if ( !isFSA(action) ) {
18 | return next(action)
19 | }
20 |
21 | const { meta = {} } = action
22 | const debouncer = debouncers[meta.debounce]
23 |
24 | if ( debouncer ) {
25 | return debouncer(action)
26 | }
27 |
28 | return next(action)
29 | }
30 | }
31 |
32 | export default debounceMiddleware
33 |
--------------------------------------------------------------------------------
/example/src/containers/AppContainer.jsx:
--------------------------------------------------------------------------------
1 | import * as exampleActions from '../redux/reducers/example'
2 | import { connect } from 'react-redux'
3 | import Input from '../components/Input'
4 | import Output from '../components/Output'
5 | import React, { PropTypes } from 'react'
6 | import Spacer from '../components/Spacer'
7 | import Title from '../components/Title'
8 |
9 | export const AppContainer = ({ input, value }) => (
10 |
11 |
12 |
13 | input(target.value)}
16 | />
17 |
18 |
19 |
20 | )
21 |
22 | AppContainer.propTypes = {
23 | input: PropTypes.func,
24 | value: PropTypes.string,
25 | }
26 |
27 | export const mapStateToProps = ({ example }) => ({
28 | value: example,
29 | })
30 |
31 | export default connect(mapStateToProps, exampleActions)(AppContainer)
32 |
--------------------------------------------------------------------------------
/example/src/server/index.js:
--------------------------------------------------------------------------------
1 | import config from '../../webpack.config'
2 | import express from 'express'
3 | import webpack from 'webpack'
4 |
5 | const app = express()
6 | const compiler = webpack(config)
7 |
8 | app.use(require('webpack-dev-middleware')(compiler, {
9 | noInfo: true,
10 | publicPath: config.output.publicPath,
11 | }))
12 |
13 | app.get('*', ( req, res ) =>
14 | res.send(`
15 |
16 |
17 |
18 | redux-debounce Example
19 |
20 |
21 |
22 |
23 |
24 |
25 | `)
26 | )
27 |
28 | app.listen(3000, 'localhost', listenErr => {
29 | if ( listenErr ) {
30 | console.log(listenErr) // eslint-disable-line
31 |
32 | return
33 | }
34 |
35 | console.log('Listening at http://localhost:3000') // eslint-disable-line
36 | })
37 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015-2016 Neil Kistner (neilkistner.com)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-debounce-example",
3 | "private": true,
4 | "version": "0.0.0",
5 | "description": "A redux-debounce example.",
6 | "license": "MIT",
7 | "repository": "wyze/redux-debounce",
8 | "author": {
9 | "name": "Neil Kistner",
10 | "email": "neil.kistner@gmail.com",
11 | "url": "neilkistner.com"
12 | },
13 | "engines": {
14 | "node": ">=0.12.0"
15 | },
16 | "scripts": {
17 | "pretest": "eslint src test",
18 | "start": "node src/server.js",
19 | "test": "nyc ava",
20 | "test:watch": "npm test -- --watch"
21 | },
22 | "ava": {
23 | "babel": "inherit",
24 | "require": [
25 | "babel-register"
26 | ]
27 | },
28 | "babel": {
29 | "presets": [
30 | "es2015",
31 | "react",
32 | "stage-2"
33 | ]
34 | },
35 | "eslintConfig": {
36 | "extends": "wyze"
37 | },
38 | "dependencies": {
39 | "express": "^4.13.4",
40 | "react": "^15.0.1",
41 | "react-dom": "^15.0.1",
42 | "react-redux": "^4.4.5",
43 | "redux": "^3.4.0"
44 | },
45 | "devDependencies": {
46 | "ava": "^0.14.0",
47 | "babel-eslint": "^6.0.2",
48 | "babel-loader": "^6.2.4",
49 | "babel-preset-es2015": "^6.6.0",
50 | "babel-preset-react": "^6.5.0",
51 | "babel-preset-stage-2": "^6.5.0",
52 | "babel-register": "^6.7.2",
53 | "enzyme": "^2.2.0",
54 | "eslint": "^2.8.0",
55 | "eslint-config-wyze": "^1.3.0",
56 | "eslint-plugin-react": "^4.2.3",
57 | "nyc": "^6.4.0",
58 | "react-addons-test-utils": "^15.0.1",
59 | "webpack": "^1.13.0",
60 | "webpack-dev-middleware": "^1.6.1"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-debounce",
3 | "version": "1.0.1",
4 | "description": "FSA-compliant middleware for Redux to debounce actions.",
5 | "license": "MIT",
6 | "repository": "wyze/redux-debounce",
7 | "author": {
8 | "name": "Neil Kistner",
9 | "email": "neil.kistner@gmail.com",
10 | "url": "neilkistner.com"
11 | },
12 | "main": "lib/index.js",
13 | "engines": {
14 | "node": ">=0.12.0"
15 | },
16 | "scripts": {
17 | "build": "babel src --out-dir lib",
18 | "clean": "rimraf .nyc_output coverage lib",
19 | "coverage": "nyc report -r text-lcov | codecov",
20 | "lint": "eslint src test",
21 | "prebuild": "npm run lint",
22 | "pretest": "npm run clean && npm run build",
23 | "test": "nyc ava",
24 | "test:watch": "npm test -- --watch"
25 | },
26 | "ava": {
27 | "babel": "inherit",
28 | "require": [
29 | "babel-register"
30 | ]
31 | },
32 | "babel": {
33 | "presets": [
34 | "es2015",
35 | "stage-2"
36 | ]
37 | },
38 | "eslintConfig": {
39 | "extends": "wyze/base"
40 | },
41 | "files": [
42 | "lib",
43 | "license",
44 | "package.json",
45 | "readme.md"
46 | ],
47 | "keywords": [
48 | "redux",
49 | "debounce",
50 | "middleware",
51 | "redux-middleware",
52 | "fsa",
53 | "flux"
54 | ],
55 | "dependencies": {
56 | "flux-standard-action": "^0.6.1",
57 | "lodash.debounce": "^4.0.8",
58 | "lodash.mapvalues": "^4.6.0"
59 | },
60 | "devDependencies": {
61 | "ava": "^0.16.0",
62 | "babel-cli": "^6.11.4",
63 | "babel-eslint": "^6.1.2",
64 | "babel-preset-es2015": "^6.13.2",
65 | "babel-preset-stage-2": "^6.13.0",
66 | "babel-register": "^6.11.6",
67 | "codecov.io": "^0.1.6",
68 | "eslint": "^3.3.1",
69 | "eslint-config-airbnb": "^10.0.1",
70 | "eslint-config-wyze": "^3.0.0",
71 | "eslint-plugin-import": "^1.13.0",
72 | "eslint-plugin-wyze": "^2.0.0",
73 | "nyc": "^8.1.0",
74 | "rimraf": "^2.5.4",
75 | "sinon": "^1.17.5"
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # redux-debounce
2 |
3 | [![Build Status][travis-image]][travis-url]
4 | [![npm][npm-image]][npm-url]
5 | [![Codecov.io][codecov-image]][codecov-url]
6 |
7 | > FSA-compliant middleware for Redux to debounce actions.
8 |
9 | ## Installation
10 |
11 | ```sh
12 | $ npm install --save redux-debounce
13 | ```
14 |
15 | ## Usage
16 |
17 | ```javascript
18 | // Store setup
19 | import { applyMiddleware, createStore } from 'redux'
20 | import createDebounce from 'redux-debounce'
21 | import createLogger from 'redux-logger'
22 | import promise from 'redux-promise'
23 | import thunk from 'redux-thunk'
24 |
25 | const config = {
26 | simple: 300
27 | }
28 |
29 | const debouncer = createDebounce(config)
30 | const logger = createLogger()
31 | const createMiddleware = applyMiddleware(thunk, debouncer, promise, logger)
32 | const store = createMiddleware(createStore)(reducer)
33 |
34 | const debounceAction = () => ({
35 | meta: {
36 | debounce: 'simple',
37 | },
38 | type: 'TEST',
39 | })
40 | ```
41 |
42 | Debounce middleware **should be** placed near the top of the chain.
43 |
44 | ### Example
45 |
46 | See the example directory.
47 |
48 | ```sh
49 | $ cd example
50 | $ npm install
51 | $ npm start
52 | ```
53 |
54 | ## API
55 |
56 | `redux-debounce` exposes single constructor function for creating debounce middleware.
57 |
58 | > createDebounce( options: Object )
59 |
60 | ### Options
61 |
62 | > **Each option is a property to setup different debounces for different actions.**
63 |
64 | #### Number
65 |
66 | Number of milliseconds to debounce the action for.
67 |
68 | #### Object
69 |
70 | ##### wait (Number)
71 |
72 | Number of milliseconds to debounce the action for.
73 |
74 | ##### maxWait (Number)
75 |
76 | Maximum number of milliseconds before the action is called.
77 |
78 | See [lodash][lodash-url] for the rest of the supported options.
79 |
80 | ## License
81 |
82 | Copyright © 2015-2016 [Neil Kistner](//github.com/wyze)
83 |
84 | Released under the MIT license. See [license](license) for details.
85 |
86 | [lodash-url]: https://lodash.com/docs#debounce
87 |
88 | [travis-image]: https://img.shields.io/travis/wyze/redux-debounce.svg?style=flat-square
89 | [travis-url]: https://travis-ci.org/wyze/redux-debounce
90 |
91 | [npm-image]: https://img.shields.io/npm/v/redux-debounce.svg?style=flat-square
92 | [npm-url]: https://npmjs.com/package/redux-debounce
93 |
94 | [codecov-image]: https://img.shields.io/codecov/c/github/wyze/redux-debounce.svg?style=flat-square
95 | [codecov-url]: https://codecov.io/github/wyze/redux-debounce
96 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | import { spy } from 'sinon'
2 | import debounceMiddleware from '../src'
3 | import test from 'ava'
4 |
5 | const config = {
6 | simple: 100,
7 | nowait: {},
8 | maxwait: { wait: 100, maxWait: 150 },
9 | }
10 | const nextHandler = debounceMiddleware(config)()
11 |
12 | test('returns a function to handle next', t => {
13 | t.is(typeof nextHandler, 'function')
14 | t.is(nextHandler.length, 1)
15 | })
16 |
17 | test('handle next returns function to handle action', t => {
18 | const actionHandler = nextHandler(spy())
19 |
20 | t.is(typeof actionHandler, 'function')
21 | t.is(actionHandler.length, 1)
22 | })
23 |
24 | test('calls next when not flux standard action', t => {
25 | const next = spy()
26 | const actionHandler = nextHandler(next)
27 | const action = { id: 1 }
28 |
29 | actionHandler(action)
30 |
31 | t.truthy(next.called)
32 | t.truthy(next.calledWith({ id: 1 }))
33 | })
34 |
35 | test.cb('calls debounce when config is passed a number', t => {
36 | const next = spy()
37 | const actionHandler = nextHandler(next)
38 | const action = { type: 'TEST', meta: { debounce: 'simple' } }
39 |
40 | actionHandler(action)
41 |
42 | t.falsy(next.called)
43 |
44 | setTimeout(() => {
45 | t.truthy(next.called)
46 | t.truthy(next.calledWith(action))
47 | t.end()
48 | }, 105)
49 | })
50 |
51 | test.cb('only calls debounced function once', t => {
52 | const next = spy()
53 | const actionHandler = nextHandler(next)
54 | const action = { type: 'TEST', meta: { debounce: 'simple' } }
55 |
56 | actionHandler(action)
57 | actionHandler(action)
58 | actionHandler(action)
59 |
60 | setTimeout(() => {
61 | t.is(next.callCount, 1)
62 | t.end()
63 | }, 105)
64 | })
65 |
66 | test.cb('supports an object passed as config to debounce', t => {
67 | const next = spy()
68 | const actionHandler = nextHandler(next)
69 | const action = { type: 'TEST', meta: { debounce: 'nowait' } }
70 |
71 | actionHandler(action)
72 |
73 | setTimeout(() => {
74 | t.truthy(next.called)
75 | t.end()
76 | }, 100)
77 | })
78 |
79 | test.cb('supports other lodash.debounce options', t => {
80 | const next = spy()
81 | const actionHandler = nextHandler(next)
82 | const action = { type: 'TEST', meta: { debounce: 'maxwait' } }
83 |
84 | actionHandler(action)
85 |
86 | setTimeout(() => {
87 | actionHandler(action)
88 | }, 75)
89 |
90 | setTimeout(() => {
91 | t.falsy(next.called)
92 | }, 100)
93 |
94 | setTimeout(() => {
95 | t.truthy(next.called)
96 | t.end()
97 | }, 155)
98 | })
99 |
100 | test('skips debounce if not passed matching key', t => {
101 | const next = spy()
102 | const actionHandler = nextHandler(next)
103 | const action = { type: 'TEST', meta: { debounce: 'nomatch' } }
104 |
105 | actionHandler(action)
106 | t.truthy(next.called)
107 | })
108 |
--------------------------------------------------------------------------------