Đặt vấn đề
Trong quá trình xây dựng các ứng dụng với ReactJS
, chúng ta luôn phải đau đầu chú ý tới performance để tránh việc API calls
, async requests
, DOM updates
,… quá nhiều lần qua các React features như shouldComponentUpdate()
, React.PureComponent
, React.memo
hay Hooks
(useState()
, useMemo()
, useContext()
, useReducer()
, etc.
Code “chạy được” là một câu chuyện, code “xịn” lại là một câu chuyện khác.
Trong bài viết này, chúng mình sẽ cùng xem xét một cách cải thiện hiệu suất của các React app mà không sử dụng bất kỳ các React features nào kể trên, thay vào đó là một kỹ thuật chung không chỉ áp dụng cho React
: Throttling và Debouncing.
Bắt đầu thôi nàooooo ??))
Bắt đầu với ví dụ
Search Box
Ta bắt đầu với một ví dụ nhé:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<span class="token keyword">import</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token string">'./autocomp.css'</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">SearchBox</span> <span class="token keyword">extends</span> <span class="token class-name">React<span class="token punctuation">.</span>Component</span> <span class="token punctuation">{</span> <span class="token function">constructor</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>state<span class="token operator">=</span> <span class="token punctuation">{</span> results<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token function-variable function">handleInput</span> <span class="token operator">=</span> evt <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> value <span class="token operator">=</span> evt<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`/api/users`</span></span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>res <span class="token operator">=></span> res<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>result <span class="token operator">=></span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">setState</span><span class="token punctuation">(</span><span class="token punctuation">{</span> results<span class="token punctuation">:</span> result<span class="token punctuation">.</span>users <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> <span class="token punctuation">{</span> results <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>state<span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">'autocomp_wrapper'</span><span class="token operator">></span> <span class="token operator"><</span>input placeholder<span class="token operator">=</span><span class="token string">"Enter your search.."</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token keyword">this</span><span class="token punctuation">.</span>handleInput<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>div<span class="token operator">></span> <span class="token punctuation">{</span>results<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>item<span class="token operator">=></span><span class="token punctuation">{</span>item<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> SearchBox<span class="token punctuation">;</span> |
Trong ví dụ trên, SearchBox
, khi bạn gõ một từ khóa nào trong ô input
, nó sẽ gửi API request
để lấy danh sách users
ra và hiển thị. Điều này có nghĩa là cứ sau mỗi ký tự bạn gõ sẽ có 1 request
gửi lên, nếu thành công, DOM
lại được update
sau lời gọi setState()
.
Như vậy, khi bạn gõ 10 ký tự thì sẽ tương ứng 10 API requests
và 10 lần updates DOM
. Mà đó là chúng ta đang xét mới chỉ một user
thôi đó ??. Bất chấp cả database
lưu dưới local
thì việc update DOM
sau mỗi ký tự cũng vô cùng “tổn phí” đúng không nào ??
Use & attachment of events
Một ví dụ khác là việc ta dùng kèm với sự kiện resize
|| scroll
. Đa phần, một trang web
được cuộn ~1000 lần/s
.
Giả sử ta có đoạn code như sau:
1 2 3 4 |
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'scroll'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Scrolled !!!'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
Hàm này sẽ được gọi ~1000 lần/s
?? Trường hợp xấu nhất là trình xử lý sự kiện phải thực hiện các tính toán và thao tác DOM nặng nề.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<span class="token keyword">function</span> <span class="token function">longOp</span><span class="token punctuation">(</span>ms<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> now <span class="token operator">=</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">var</span> end <span class="token operator">=</span> now <span class="token operator">+</span> ms <span class="token keyword">while</span><span class="token punctuation">(</span>now <span class="token operator"><</span> end<span class="token punctuation">)</span> <span class="token punctuation">{</span> now <span class="token operator">=</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'scroll'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// simulating a heavy operation</span> <span class="token function">longOp</span><span class="token punctuation">(</span><span class="token number">9000</span><span class="token punctuation">)</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Scrolled !!!'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
Như bạn đã thấy, sau 9s
hệ thống sẽ log
ra Scrolled !!!
. Nếu chúng ta cuộn body
tới 5000px
, sẽ có tới 200+
sự kiện bị gọi. Mỗi sự kiện cần 9s để kết thúc. Vậy là sau 9 * 200 = 1800s
để hoàn thành hết 200+
sự kiện. Do đó, mất tới nửa giờ từ lúc bắt đầu cho tới kết thúc.
Chắc chắn kết quả sẽ không ngọt ngào rằng brouser
sẽ “ổn” đâu, nó có thể bị lag
hoặc không phản hồi ??
Hmmm… nhận ra vấn đề ở đây rồi đúng không nào ??
Cùng tìm hiểu base
throttling
& debouncing
chút đã nhé ??
Throttling
Throttling enforces a maximum number of times a function can be called over time
Throttling là việc điều chỉnh thực thi một chức năng nhất định sau khi một khoảng thời gian xác định đã trôi qua.
Ví dụ như “Chỉ thực thi hàm này nhiều nhất 1 lần trong 100ms”.
Hay giả sử như mình gọi một hàm với tốc độ 1000 lần/20s
. Nếu chúng ta điều tiết để thực thi trong mỗi 500ms
, thì trong 20s
, chức năng sẽ được thực thi trong 40 lần/20s
:
1 2 3 |
<span class="token number">1000</span> <span class="token operator">*</span> <span class="token number">20</span> secs <span class="token operator">=</span> <span class="token number">20</span><span class="token punctuation">,</span><span class="token number">000</span>ms <span class="token number">20</span><span class="token punctuation">,</span><span class="token number">000</span>ms <span class="token operator">/</span> <span class="token number">500</span>ms <span class="token operator">=</span> <span class="token number">40</span> times |
Từ 20000
xuống 40
, đáng kể chưaaaaa ??
Để ứng dụng Throttling
trong React
, chúng ta sẽ sử dụng underscore
, lodash libraries
, RxJS
& tùy chỉnh riêng.
underscore
Thư viện underscore
là một package trên npm, dùng để điều tiết component
.
1 2 |
npm i underscore |
Ta có thể sử dụng trong component
như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<span class="token comment">// ...</span> <span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> _ <span class="token keyword">from</span> underscore<span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">SearchBox</span> <span class="token keyword">extends</span> <span class="token class-name">React<span class="token punctuation">.</span>Component</span> <span class="token punctuation">{</span> <span class="token function">constructor</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>state <span class="token operator">=</span> <span class="token punctuation">{</span> results<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token keyword">this</span><span class="token punctuation">.</span>handleInputThrottled <span class="token operator">=</span> _<span class="token punctuation">.</span><span class="token function">throttle</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>handleInput<span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function-variable function">handleInput</span> <span class="token operator">=</span> evt <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> value <span class="token operator">=</span> evt<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value <span class="token keyword">const</span> filteredRes <span class="token operator">=</span> data<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// algorithm to search through the `data` array</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">setState</span><span class="token punctuation">(</span><span class="token punctuation">{</span> results<span class="token punctuation">:</span> filteredRes <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> <span class="token punctuation">{</span> results <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>state<span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">'autocomp_wrapper'</span><span class="token operator">></span> <span class="token operator"><</span>input placeholder<span class="token operator">=</span><span class="token string">"Enter your search.."</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token keyword">this</span><span class="token punctuation">.</span>handleInputThrottled<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>div<span class="token operator">></span> <span class="token punctuation">{</span>results<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>result<span class="token operator">=></span><span class="token punctuation">{</span>result<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Trong đoạn code trên, hàm điều tiết handleInputThrottled()
nhận vào một callback
là handleInput()
(hàm cần được điều tiết) và một timebox
.
Trở lại với ví dụ phía trên, giả sử tốc độ gõ bình thường của một kí tự là 200ms, gõ 10 ký tự sẽ tốn 200 x 10 = 2000ms
. Hàm handleInput
bây giờ sẽ chỉ được gọi 2000 / 1000 = 2
lần thôi, thay vì 10 lần như trước.
lodash
lodash
cũng là một thư viện giúp chúng ta xử lý vấn đề này.
1 2 |
npm i lodash |
Với ví dụ đầu tiên:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<span class="token comment">// ...</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> throttle <span class="token punctuation">}</span> <span class="token keyword">from</span> lodash<span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">SearchBox</span> <span class="token keyword">extends</span> <span class="token class-name">React<span class="token punctuation">.</span>Component</span> <span class="token punctuation">{</span> <span class="token function">constructor</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token keyword">this</span><span class="token punctuation">.</span>handleInputThrottled <span class="token operator">=</span> <span class="token function">throttle</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>handleInput<span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function-variable function">handleInput</span> <span class="token operator">=</span> evt <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span> |
Chả khác gì ngoài thay hàm throttle
bên lodash
với _.throttle
bên underscore
cả ??
RxJS
RxJS
là Reactive Extensions in JS
cung cấp cho chúng ta các toán tử, trong đó có một toán tử xử lý vấn đề throttling
.
1 2 |
npm i rxjs |
Ví dụ đầu tiên sẽ được xử lý như sau với RxJS
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<span class="token comment">// ...</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> BehaviorSubject <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'rxjs'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> throttle <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'rxjs/operators'</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">SearchBox</span> <span class="token keyword">extends</span> <span class="token class-name">React<span class="token punctuation">.</span>Component</span> <span class="token punctuation">{</span> <span class="token function">constructor</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>state <span class="token operator">=</span> <span class="token punctuation">{</span> results<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token keyword">this</span><span class="token punctuation">.</span>inputStream <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BehaviorSubject</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">componentDidMount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>inputStream <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span> <span class="token function">throttle</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span>v <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> filteredRes <span class="token operator">=</span> data<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// algorithm to search through the `data` array</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">setState</span><span class="token punctuation">(</span><span class="token punctuation">{</span> results<span class="token punctuation">:</span> filteredRes <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> <span class="token punctuation">{</span> results <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>state<span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">'autocomp_wrapper'</span><span class="token operator">></span> <span class="token operator"><</span>input placeholder<span class="token operator">=</span><span class="token string">"Enter your search.."</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span>e <span class="token operator">=></span> <span class="token keyword">this</span><span class="token punctuation">.</span>inputStream<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>div<span class="token operator">></span> <span class="token punctuation">{</span>results<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>result <span class="token operator">=></span> <span class="token punctuation">{</span> result <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Chúng ta import throttle
& BehaviorSubject
từ thư viện RxJS
. Đầu tiên, khởi tạo một inputStream property
là một BehaviorSubject instance
.
Khi bắt đầu gõ ký tự vào input là lúc emit giá trị đó vào inputStream.
Trong componentDidMount
, cho inputStream
đi qua một pipe
với throttle(1000)
(nghĩa là RxJS
sẽ điều tiết inputStream
sau mỗi 1000ms
), sau đó trả về một Observable
, ta subscrible
để lấy được giá trị đó.
Tự viết custom implementation
Để hiểu hơn về cơ chế throttling
, có lẽ chúng ta nên tự viết throttling implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<span class="token keyword">function</span> <span class="token function">throttle</span><span class="token punctuation">(</span>fn<span class="token punctuation">,</span> ms<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> timeout <span class="token keyword">function</span> <span class="token function">exec</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fn<span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> timeout <span class="token operator">==</span> undefined <span class="token operator">?</span> <span class="token keyword">null</span> <span class="token punctuation">:</span> <span class="token function">clearTimeout</span><span class="token punctuation">(</span>timeout<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">if</span><span class="token punctuation">(</span>fn <span class="token operator">!==</span> undefined <span class="token operator">&&</span> ms <span class="token operator">!==</span> undefined<span class="token punctuation">)</span> <span class="token punctuation">{</span> timeout <span class="token operator">=</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span>exec<span class="token punctuation">,</span> ms<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">'callback function and the timeout must be supplied'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// API to clear the timeout</span> throttle<span class="token punctuation">.</span><span class="token function-variable function">clearTimeout</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Trong component SearchBox ta chỉ cần:
1 2 3 |
<span class="token comment">// Trong constructor()</span> <span class="token keyword">this</span><span class="token punctuation">.</span>handleInputThrottled <span class="token operator">=</span> <span class="token function">throttle</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>handleInput<span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span> |
là được rồi ??
Debouncing
Debouncing enforces that a function will not be called again until a certain amount of time has passed since its last call.
Trong Debouncing
, nó bỏ qua tất cả các lệnh gọi đến một hàm và đợi cho đến khi hàm đó ngừng được gọi trong một khoảng thời gian xác định.
Về áp dụng, cú pháp giống y throtting
luôn, mình có thể dùng lodash
, underscore
hay RxJS
:
1 2 3 4 5 |
<span class="token comment">// Case 1</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> debounce <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'lodash'</span><span class="token punctuation">;</span> <span class="token operator">...</span> <span class="token keyword">this</span><span class="token punctuation">.</span>handleInputThrottled <span class="token operator">=</span> <span class="token function">debounce</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>handleInput<span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span> |
1 2 3 4 |
<span class="token comment">// Case 2</span> <span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> _ <span class="token keyword">from</span> <span class="token string">'underscore'</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>handleInputThrottled <span class="token operator">=</span> _<span class="token punctuation">.</span><span class="token function">debounce</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>handleInput<span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<span class="token comment">// Case 3</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> BehaviorSubject <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'rxjs'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> debounce <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'rxjs/operators'</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">SearchBox</span> <span class="token keyword">extends</span> <span class="token class-name">React<span class="token punctuation">.</span>Component</span> <span class="token punctuation">{</span> <span class="token function">constructor</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token keyword">this</span><span class="token punctuation">.</span>inputStream <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BehaviorSubject</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">componentDidMount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>inputStream <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span> <span class="token function">debounce</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span>v <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Bạn cũng có thể tham khảo một demo
sử dụng debounce
trong lodash
với Functional Component
tại đây.
Common cases
Các trường hợp hay sử dụng tới throtting
hay deboucing
ta có thể kể tới như trong các Game
, đặc biệt là các game hành động yêu cầu nhấn phím hoặc thực hiện các hành động như bắn súng, tăng tốc,… game thủ có thể bấm một phím thường xuyên (40 lần trong 20 giây tức là 2 lần một giây) nhưng cho dù game thủ nhấn phím bắn bao nhiêu lần thì nó cũng sẽ chỉ bắn một lần (giả sử nói mỗi giây).
Ngoài ra thì trường hợp SearchBox
như trên cũng thường được sử dụng throtting
hay deboucing
khá nhiều để hạn chế các API calls
, như một cách để giảm tải cho server
chẳng hạn. ??
Kết
Thay vì phải gọi liên tục gọi các phương thức trong ứng dụng React
, Throtting
hay Deboucing
thực sự là một giải pháp tốt để xử lý, nâng cao hiệu suất, tránh các trường hợp DOM-re-rendering
không cần thiết các node
.
Cảm ơn các bạn vì đã đọc bài viết của mình, tặng mình một upvote
để có thêm động lực cho các chủ đề sắp tới nhaaaaa ^^
Tham khảo thêm các bài viết liên quan tại đây. Nếu có ý kiến bổ sung hay bất kỳ câu hỏi nào liên quan đến vấn đề này, hãy comment
phía dưới cho mình nhé !
Chúc bạn một ngày làm việc hiệu quả ???
Happy coding !
References: Medium