From 44e5f6a1ddc19da6d6112df9b33dba3b90f91dd6 Mon Sep 17 00:00:00 2001 From: Alex Krolick Date: Sun, 19 Nov 2017 21:01:51 -0800 Subject: [PATCH 1/5] Add debounce and throttle examples for event handlers --- content/docs/faq-functions.md | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/content/docs/faq-functions.md b/content/docs/faq-functions.md index 6cb41a57..9d9bb108 100644 --- a/content/docs/faq-functions.md +++ b/content/docs/faq-functions.md @@ -177,3 +177,52 @@ class Alphabet extends React.Component { } } ``` + +### How can I prevent a function from being called too quickly or too many times in a row? + +If you have an event handler such as `onClick` or `onScroll` and want to prevent the callback from being fired too quickly, you can wrap the handler with a utility such as [`_.debounce`](https://lodash.com/docs#debounce) or [`_.throttle`](https://lodash.com/docs#throttle). For a given `delay`, say `100ms`, debouncing calls the handler after activity stops for that amount of time; throttling prevents the handler from being called more than once per `delay`. See a visualization [here](http://demo.nimius.net/debounce_throttle/). + +#### Throttle + +```jsx +import { throttle } from 'lodash' + +class LoadMoreButton extends React.Component { + handleClick = throttle(() => { + this.props.loadMore() + }, 100) + + render() { + return + } +} +``` + +#### Debounce + +```jsx +import { debounce } from 'lodash' + +class Searchbox extends React.Component { + handleChange = event => { + event.persist() + this._handleChangeDebounced(event) + }; + + _handleChangeDebounced = debounce(event => { + this.props.onChange(event.target.value) + }, 250) + }; + + render() { + return ( + + ) + } +} +``` \ No newline at end of file From c17b54a121742933f8afa7559ff8a7dda70cef29 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 20 Nov 2017 11:10:50 -0800 Subject: [PATCH 2/5] Update debounce/throttle examples --- content/docs/faq-functions.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/content/docs/faq-functions.md b/content/docs/faq-functions.md index 9d9bb108..289c5621 100644 --- a/content/docs/faq-functions.md +++ b/content/docs/faq-functions.md @@ -185,7 +185,7 @@ If you have an event handler such as `onClick` or `onScroll` and want to prevent #### Throttle ```jsx -import { throttle } from 'lodash' +import throttle from 'lodash.throttle' class LoadMoreButton extends React.Component { handleClick = throttle(() => { @@ -201,18 +201,17 @@ class LoadMoreButton extends React.Component { #### Debounce ```jsx -import { debounce } from 'lodash' +import debounce from 'lodash.debounce' class Searchbox extends React.Component { handleChange = event => { event.persist() - this._handleChangeDebounced(event) + this._handleChangeDebounced(event.target.value) }; - _handleChangeDebounced = debounce(event => { - this.props.onChange(event.target.value) - }, 250) - }; + _handleChangeDebounced = debounce(value => { + this.props.onChange(value) + }, 250) render() { return ( @@ -225,4 +224,4 @@ class Searchbox extends React.Component { ) } } -``` \ No newline at end of file +``` From 95c03c0fc89530a218f0f28c0232fbd7a585ee94 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 20 Nov 2017 11:17:58 -0800 Subject: [PATCH 3/5] Add links to event pooling docs --- content/docs/faq-functions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/content/docs/faq-functions.md b/content/docs/faq-functions.md index 289c5621..d276def3 100644 --- a/content/docs/faq-functions.md +++ b/content/docs/faq-functions.md @@ -182,6 +182,8 @@ class Alphabet extends React.Component { If you have an event handler such as `onClick` or `onScroll` and want to prevent the callback from being fired too quickly, you can wrap the handler with a utility such as [`_.debounce`](https://lodash.com/docs#debounce) or [`_.throttle`](https://lodash.com/docs#throttle). For a given `delay`, say `100ms`, debouncing calls the handler after activity stops for that amount of time; throttling prevents the handler from being called more than once per `delay`. See a visualization [here](http://demo.nimius.net/debounce_throttle/). +Note: Call `event.persist()` when accessing events asynchronously to prevent the synthetic event from being recycled by [event pooling](/docs/events.html#event-pooling). See the debounce [example](#debounce) below. + #### Throttle ```jsx @@ -205,7 +207,7 @@ import debounce from 'lodash.debounce' class Searchbox extends React.Component { handleChange = event => { - event.persist() + event.persist() // https://reactjs.org/docs/events.html#event-pooling this._handleChangeDebounced(event.target.value) }; From 78c285f86b67866c618cc621f27d090b4c9a7485 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 20 Nov 2017 11:27:54 -0800 Subject: [PATCH 4/5] Inline event.persist explanation --- content/docs/faq-functions.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content/docs/faq-functions.md b/content/docs/faq-functions.md index d276def3..d0021427 100644 --- a/content/docs/faq-functions.md +++ b/content/docs/faq-functions.md @@ -182,8 +182,6 @@ class Alphabet extends React.Component { If you have an event handler such as `onClick` or `onScroll` and want to prevent the callback from being fired too quickly, you can wrap the handler with a utility such as [`_.debounce`](https://lodash.com/docs#debounce) or [`_.throttle`](https://lodash.com/docs#throttle). For a given `delay`, say `100ms`, debouncing calls the handler after activity stops for that amount of time; throttling prevents the handler from being called more than once per `delay`. See a visualization [here](http://demo.nimius.net/debounce_throttle/). -Note: Call `event.persist()` when accessing events asynchronously to prevent the synthetic event from being recycled by [event pooling](/docs/events.html#event-pooling). See the debounce [example](#debounce) below. - #### Throttle ```jsx @@ -207,9 +205,11 @@ import debounce from 'lodash.debounce' class Searchbox extends React.Component { handleChange = event => { - event.persist() // https://reactjs.org/docs/events.html#event-pooling - this._handleChangeDebounced(event.target.value) - }; + // React pools events, so we read the value before debounce. + // Alternately we could call `event.persist()` + // For more info see reactjs.org/docs/events.html#event-pooling + this._handleChangeDebounced(event.target.value); + } _handleChangeDebounced = debounce(value => { this.props.onChange(value) From 0ba8920310883b0135259b21106341d4e8b3d029 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 20 Nov 2017 13:03:33 -0800 Subject: [PATCH 5/5] Added some additional wording --- content/docs/faq-functions.md | 52 +++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/content/docs/faq-functions.md b/content/docs/faq-functions.md index d0021427..bc6619b0 100644 --- a/content/docs/faq-functions.md +++ b/content/docs/faq-functions.md @@ -180,50 +180,66 @@ class Alphabet extends React.Component { ### How can I prevent a function from being called too quickly or too many times in a row? -If you have an event handler such as `onClick` or `onScroll` and want to prevent the callback from being fired too quickly, you can wrap the handler with a utility such as [`_.debounce`](https://lodash.com/docs#debounce) or [`_.throttle`](https://lodash.com/docs#throttle). For a given `delay`, say `100ms`, debouncing calls the handler after activity stops for that amount of time; throttling prevents the handler from being called more than once per `delay`. See a visualization [here](http://demo.nimius.net/debounce_throttle/). +If you have an event handler such as `onClick` or `onScroll` and want to prevent the callback from being fired too quickly, you can wrap the handler with a utility such as [`_.debounce`](https://lodash.com/docs#debounce) or [`_.throttle`](https://lodash.com/docs#throttle). See [this visualization](http://demo.nimius.net/debounce_throttle/) for a comparison of the two. + +> Note: +> +> Both `_.debounce` and `_.throttle` provide a `cancel` method to cancel delayed callbacks. You should either call this method from `componentWillUnmount` _or_ check to ensure that the component is still mounted within the delayed function. #### Throttle +Throttling prevents a function from being called more than once in a given window of time. The example below throttles a "click" handler to prevent calling it more than once per second. + ```jsx -import throttle from 'lodash.throttle' +import throttle from "lodash.throttle"; class LoadMoreButton extends React.Component { - handleClick = throttle(() => { - this.props.loadMore() - }, 100) + componentWillUnmount() { + this._handleClick.cancel(); + } render() { - return + return ; } + + _handleClick = throttle(() => { + this.props.loadMore(); + }, 1000); } ``` #### Debounce +Debouncing ensures that a function will not be executed until after a certain amount of time has passed since it was last called. This can be useful when you have to perform some expensive calculation in response to an event that might dispatch rapidly (eg scroll or keyboard events). The example below debounces text input with a 250ms delay. + ```jsx -import debounce from 'lodash.debounce' +import debounce from "lodash.debounce"; class Searchbox extends React.Component { - handleChange = event => { - // React pools events, so we read the value before debounce. - // Alternately we could call `event.persist()` - // For more info see reactjs.org/docs/events.html#event-pooling - this._handleChangeDebounced(event.target.value); + componentWillUnmount() { + this._handleChangeDebounced.cancel(); } - _handleChangeDebounced = debounce(value => { - this.props.onChange(value) - }, 250) - render() { return ( - ) + ); } + + _handleChange = event => { + // React pools events, so we read the value before debounce. + // Alternately we could call `event.persist()` and pass the entire event. + // For more info see reactjs.org/docs/events.html#event-pooling + this._handleChangeDebounced(event.target.value); + }; + + _handleChangeDebounced = debounce(value => { + this.props.onChange(value); + }, 250); } ```