diff --git a/frontend/pages/ForgotPasswordPage/ForgotPasswordPage.jsx b/frontend/pages/ForgotPasswordPage/ForgotPasswordPage.jsx index dacd7f4170..5a2e2a0449 100644 --- a/frontend/pages/ForgotPasswordPage/ForgotPasswordPage.jsx +++ b/frontend/pages/ForgotPasswordPage/ForgotPasswordPage.jsx @@ -6,6 +6,7 @@ import { clearForgotPasswordErrors, forgotPasswordAction, } from '../../redux/nodes/components/ForgotPasswordPage/actions'; +import debounce from '../../utilities/debounce'; import ForgotPasswordForm from '../../components/forms/ForgotPasswordForm'; import Icon from '../../components/icons/Icon'; import StackedWhiteBoxes from '../../components/StackedWhiteBoxes'; @@ -21,11 +22,11 @@ export class ForgotPasswordPage extends Component { dispatch: noop, }; - onSubmit = (formData) => { + onSubmit = debounce((formData) => { const { dispatch } = this.props; return dispatch(forgotPasswordAction(formData)); - } + }) clearErrors = () => { const { dispatch } = this.props; diff --git a/frontend/pages/LoginPage/LoginPage.jsx b/frontend/pages/LoginPage/LoginPage.jsx index d68d6cedcf..e0223f1a56 100644 --- a/frontend/pages/LoginPage/LoginPage.jsx +++ b/frontend/pages/LoginPage/LoginPage.jsx @@ -2,6 +2,7 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { push } from 'react-router-redux'; import componentStyles from './styles'; +import debounce from '../../utilities/debounce'; import local from '../../utilities/local'; import LoginForm from '../../components/forms/LoginForm'; import { loginUser } from '../../redux/nodes/auth/actions'; @@ -24,13 +25,13 @@ export class LoginPage extends Component { return false; } - onSubmit = (formData) => { + onSubmit = debounce((formData) => { const { dispatch } = this.props; return dispatch(loginUser(formData)) .then(() => { return dispatch(push('/login_successful')); }); - } + }) render () { const { formWrapperStyles, whiteTabStyles } = componentStyles; diff --git a/frontend/pages/ResetPasswordPage/ResetPasswordPage.jsx b/frontend/pages/ResetPasswordPage/ResetPasswordPage.jsx index b10d6fc7cf..ea2baea00d 100644 --- a/frontend/pages/ResetPasswordPage/ResetPasswordPage.jsx +++ b/frontend/pages/ResetPasswordPage/ResetPasswordPage.jsx @@ -2,6 +2,7 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { noop } from 'lodash'; import { push } from 'react-router-redux'; +import debounce from '../../utilities/debounce'; import { resetPassword } from '../../redux/nodes/components/ResetPasswordPage/actions'; import ResetPasswordForm from '../../components/forms/ResetPasswordForm'; import StackedWhiteBoxes from '../../components/StackedWhiteBoxes'; @@ -24,7 +25,7 @@ export class ResetPasswordPage extends Component { return false; } - onSubmit = (formData) => { + onSubmit = debounce((formData) => { const { dispatch, token } = this.props; const resetPasswordData = { ...formData, @@ -35,7 +36,7 @@ export class ResetPasswordPage extends Component { .then(() => { return dispatch(push('/login')); }); - } + }) render () { const { onSubmit } = this; diff --git a/frontend/utilities/debounce/debounce.tests.js b/frontend/utilities/debounce/debounce.tests.js new file mode 100644 index 0000000000..73e1044848 --- /dev/null +++ b/frontend/utilities/debounce/debounce.tests.js @@ -0,0 +1,16 @@ +import expect from 'expect'; +import debounce from './index'; + +describe('debounce - utility', () => { + it('prevents double-clicks from executing a function multiple times', () => { + let count = 0; + const increaseCount = () => { + count += 1; + }; + const debouncedFunc = debounce(increaseCount); + + debouncedFunc(); + debouncedFunc(); + expect(count).toEqual(1); + }); +}); diff --git a/frontend/utilities/debounce/index.js b/frontend/utilities/debounce/index.js new file mode 100644 index 0000000000..b552156efd --- /dev/null +++ b/frontend/utilities/debounce/index.js @@ -0,0 +1,10 @@ +import { debounce } from 'lodash'; + +const TIMEOUT = 1000; // only allow 1 click per second + +export default (func) => { + return debounce(func, TIMEOUT, { + leading: true, + trailing: false, + }); +};