Xin chào mọi người, hôm nay mình xin giới thiệu 1 khái niệm trong ReactJS, đó là Error Boundary.
Error Boundary là gì ?
Trước đây khi có lỗi ở một component
, ReactJS sẽ trả ra những lỗi khó hiểu ở các lần render tiếp theo, gây lỗi cho toàn bộ ứng dụng, điều này khiến việc sửa và phục hồi do gặp bug rất khó khăn.
Từ bản 16
, ReactJS mang đến một khái niệm mới Error Boundary
, là một component
hỗ trợ việc xử lý lỗi.
Error Boundary có tác dụng gì ?
Nếu bạn đã từng sử dụng try ... catch
block, thì bạn có thể hiểu Error Boundary
có tính năng tương tự, nhưng thay vì áp dụng cho 1 đoạn code thì sẽ áp dụng cho component
.
Error Boundary
sẽ bắt lỗi của tất cả cả các component con, in ra các lỗi đó, và hiển thị ra một fallback UI
(Một phẩn hiển thị dự phòng).
Error Boundary bắt lỗi trong quá trình render
, trong các hàm lifecycle
, và trong hàm khởi tạo của tất cả các component
ở dưới nó (miễn là trong cùng 1 component tree
).
Error Boundary dùng thế nào ?
Bạn chỉ có thể tạo 1 Error Boundary
bằng 1 Class component.
Một Class component trở thành 1 Error Boundary nếu nó định nghĩa một hoặc cả 2 hàm sau
1 2 | <span class="token keyword">static</span> <span class="token function">getDerivedStateFromError</span><span class="token punctuation">(</span><span class="token punctuation">)</span> |
Hàm này dùng để render
một fallback UI
nếu có lỗi được trả ra.
1 2 | <span class="token function">componentDidCatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span> |
Hàm này dùng để bắt và log hoặc hiển thị ra lỗi.
Sau khi component
của bạn có cả 2 hàm này, bạn sẽ bọc nó quanh một component khác để sử dụng tính năng Error Boundary
.
Ví dụ
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 41 42 43 44 45 46 47 | <span class="token keyword">import</span> React<span class="token punctuation">,</span> <span class="token punctuation">{</span> useState <span class="token punctuation">}</span> <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">"./styles.css"</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">ErrorBoundary</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><span class="token parameter">props</span><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> hasError<span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">static</span> <span class="token function">getDerivedStateFromError</span><span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> hasError<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">componentDidCatch</span><span class="token punctuation">(</span><span class="token parameter">error<span class="token punctuation">,</span> errorInfo</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>errorInfo<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">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>state<span class="token punctuation">.</span>hasError<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span><span class="token plain-text">Some thing went wrong</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>props<span class="token punctuation">.</span>children<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> <span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>counter<span class="token punctuation">,</span> setCounter<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>counter <span class="token operator">===</span> <span class="token number">3</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">"Oops, You triggered Doomsday!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> <span class="token function-variable function">countUntilDoom</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setCounter</span><span class="token punctuation">(</span>counter <span class="token operator">+</span> <span class="token number">1</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">return</span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>App<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span><span class="token plain-text">Hello CodeSandbox</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span><span class="token plain-text">Start editing to see some magic happen!</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">onClick</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>countUntilDoom<span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text">Click on me for money</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span><span class="token punctuation">{</span>counter<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Và trong file index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <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> ReactDOM <span class="token keyword">from</span> <span class="token string">"react-dom"</span><span class="token punctuation">;</span> <span class="token keyword">import</span> App<span class="token punctuation">,</span> <span class="token punctuation">{</span> ErrorBoundary <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./App"</span><span class="token punctuation">;</span> <span class="token keyword">const</span> rootElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"root"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> ReactDOM<span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span> <span class="token operator"><</span>React<span class="token punctuation">.</span>StrictMode<span class="token operator">></span> <span class="token operator"><</span>ErrorBoundary<span class="token operator">></span> <span class="token operator"><</span>App <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>ErrorBoundary<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>React<span class="token punctuation">.</span>StrictMode<span class="token operator">></span><span class="token punctuation">,</span> rootElement <span class="token punctuation">)</span><span class="token punctuation">;</span> |
Mình xin giải thích ví dụ này như sau:
Bạn có 1 component là App
, phần render
của component
này được bọc quanh 1 Error Boundary
.
Component này có 1 button mà khi ấn đủ 3 lần nó sẽ trả ra 1 lỗi. Và thay vì màn hình trắng như mọi khi, Error Boundary sẽ bắt được lỗi của component bên trong và render ra <h1>Some thing went wrong</h1>
Error Boundary nên đặt ở đâu ?
Điều này là tùy vào người viết, có thể đặt ở tầng trên cùng của ứng dụng để bắt toàn bộ lỗi, cũng có thể đặt sâu hơn để bắt lỗi cho cụ thể.
Giới hạn của Error Boundary
Error Boundary không thể bắt lỗi cho
- Event Handler
- Các hàm chạy bất đồng bộ (
setTimeout
hoặcrequestAnimationFrame
) - Server side render
- Lỗi được phát ra từ chính nó
- Lỗi của các component ở bên trên nó
- Lỗi của component cách component tree của nó 8000 dặm
Hy vọng sau bài viết này bạn đã có 1 công cụ hữu ích giúp debug trên ứng dụng ReactJs. Bạn có thể đọc thêm ở đây .
Cảm ơn vì đã đọc.