Xin chào các bạn, tiếp tục series về React thì hôm nay mình xin chia sẻ về cách sử dụng tối ưu các hooks trong React. Thì React hooks được release vào phiên bản v16. 8.0 cách đây cũng được 4 năm rồi, khi react hooks ra đời thì đã làm thay đổi tư duy lập trình của các lập trình viên. Và hôm nay mình muốn nói về việc chuyển đổi từ class component sang function components + hooks
Vòng đời
Class component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">class</span> <span class="token class-name">Chart</span> <span class="token keyword">extends</span> <span class="token class-name">Component</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 comment">// khi đã mount Chart</span> <span class="token punctuation">}</span> <span class="token function">componentDidUpdate</span><span class="token punctuation">(</span><span class="token parameter">prevProps</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>prevProps<span class="token punctuation">.</span>data <span class="token operator">==</span> props<span class="token punctuation">.</span>data<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token comment">// khi update dữ liệu</span> <span class="token punctuation">}</span> <span class="token function">componentWillUnmount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// trước khi unmount Chart</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">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>svg className<span class="token operator">=</span><span class="token string">"Chart"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Function component
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">const</span> <span class="token function-variable function">Chart</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> data <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">useEffect</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">// khi mount Chart hoặc update dữ liệu</span> <span class="token keyword">return</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">// khi update dữ liệu</span> <span class="token comment">// trước khi unmount chart</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>data<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>svg className<span class="token operator">=</span><span class="token string">"Chart"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Cập nhật dữ liệu
Sử dụng useEffect để nắm bắt vòng đời của React.
Có phải bạn đang suy nghĩ useEffect thật là vi diêu không? Nhưng mà chưa đâu thật sự useEffect không phải là hook để giải quyết mọi vấn đề. Hãy cùng mình tìm hiểu tiếp nhá.
Bây giờ hãy xem một ví dụ khác biệt là muốn tính toán dữ liệu bằng hàm getDataWithinRange.
Class component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">class</span> <span class="token class-name">Chart</span> <span class="token keyword">extends</span> <span class="token class-name">Component</span> <span class="token punctuation">{</span> state <span class="token operator">=</span> <span class="token punctuation">{</span> data<span class="token operator">:</span> <span class="token keyword">null</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">const</span> newData <span class="token operator">=</span> <span class="token function">getDataWithinRange</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>props<span class="token punctuation">.</span>dateRange<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>data<span class="token operator">:</span> newData<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">componentDidUpdate</span><span class="token punctuation">(</span><span class="token parameter">prevProps</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>prevProps<span class="token punctuation">.</span>dateRange <span class="token operator">!=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>props<span class="token punctuation">.</span>dateRange<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> newData <span class="token operator">=</span> <span class="token function">getDataWithinRange</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>props<span class="token punctuation">.</span>dateRange<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>data<span class="token operator">:</span> newData<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">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>svg className<span class="token operator">=</span><span class="token string">"Chart"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Function component
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">const</span> <span class="token function-variable function">Chart</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> dateRange <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>data<span class="token punctuation">,</span> setData<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token function">useEffect</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 keyword">const</span> newData <span class="token operator">=</span> <span class="token function">getDataWithinRange</span><span class="token punctuation">(</span>dateRange<span class="token punctuation">)</span> <span class="token function">setData</span><span class="token punctuation">(</span>newData<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>dateRange<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>svg className<span class="token operator">=</span><span class="token string">"Chart"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
useEffect đang cập nhật data dựa vào sự thay đổi của dateRange => khi prop dateRange thay đổi thì useEffect được kích hoạt và tính toán newData bằng function getDataWithinRange sau đó cập nhật lại dữ liệu bằng hook setData.
Thay vì sử dụng 2 hook useEffect và useStatet thì ta có thể tối ưu bằng hook useMemo
1 2 3 4 5 6 7 8 9 | <span class="token keyword">const</span> <span class="token function-variable function">Chart</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> dateRange <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token function">useMemo</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 function">getDataWithinRange</span><span class="token punctuation">(</span>dateRange<span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>dateRange<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>svg className<span class="token operator">=</span><span class="token string">"Chart"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Dưới đây là 1 ví dụ thấy sự tối ưu rõ rệt về code giữa class component và function component
Ví dụ
Class component
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 keyword">class</span> <span class="token class-name">Chart</span> <span class="token keyword">extends</span> <span class="token class-name">Component</span> <span class="token punctuation">{</span> state <span class="token operator">=</span> <span class="token punctuation">{</span> data<span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span> dimensions<span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span> xScale<span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span> yScale<span class="token operator">:</span> <span class="token keyword">null</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">const</span> newData <span class="token operator">=</span> <span class="token function">getDataWithinRange</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>props<span class="token punctuation">.</span>dateRange<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>data<span class="token operator">:</span> newData<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>dimensions<span class="token operator">:</span> <span class="token function">getDimensions</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">this</span><span class="token punctuation">.</span><span class="token function">setState</span><span class="token punctuation">(</span><span class="token punctuation">{</span>xScale<span class="token operator">:</span> <span class="token function">getXScale</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">this</span><span class="token punctuation">.</span><span class="token function">setState</span><span class="token punctuation">(</span><span class="token punctuation">{</span>yScale<span class="token operator">:</span> <span class="token function">getYScale</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> <span class="token function">componentDidUpdate</span><span class="token punctuation">(</span><span class="token parameter">prevProps<span class="token punctuation">,</span> prevState</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>prevProps<span class="token punctuation">.</span>dateRange <span class="token operator">!=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>props<span class="token punctuation">.</span>dateRange<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> newData <span class="token operator">=</span> <span class="token function">getDataWithinRange</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>props<span class="token punctuation">.</span>dateRange<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>data<span class="token operator">:</span> newData<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>prevProps<span class="token punctuation">.</span>margins <span class="token operator">!=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>props<span class="token punctuation">.</span>margins<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>dimensions<span class="token operator">:</span> <span class="token function">getDimensions</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> <span class="token keyword">if</span> <span class="token punctuation">(</span>prevState<span class="token punctuation">.</span>data <span class="token operator">!=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>state<span class="token punctuation">.</span>data<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>xScale<span class="token operator">:</span> <span class="token function">getXScale</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">this</span><span class="token punctuation">.</span><span class="token function">setState</span><span class="token punctuation">(</span><span class="token punctuation">{</span>yScale<span class="token operator">:</span> <span class="token function">getYScale</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> <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">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>svg className<span class="token operator">=</span><span class="token string">"Chart"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Function component
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">const</span> <span class="token function-variable function">Chart</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> dateRange<span class="token punctuation">,</span> margins <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token function">useMemo</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 function">getDataWithinRange</span><span class="token punctuation">(</span>dateRange<span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>dateRange<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">const</span> dimensions <span class="token operator">=</span> <span class="token function">useMemo</span><span class="token punctuation">(</span>getDimensions<span class="token punctuation">,</span> <span class="token punctuation">[</span>margins<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">const</span> xScale <span class="token operator">=</span> <span class="token function">useMemo</span><span class="token punctuation">(</span>getXScale<span class="token punctuation">,</span> <span class="token punctuation">[</span>data<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">const</span> yScale <span class="token operator">=</span> <span class="token function">useMemo</span><span class="token punctuation">(</span>getYScale<span class="token punctuation">,</span> <span class="token punctuation">[</span>data<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>svg className<span class="token operator">=</span><span class="token string">"Chart"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Như ví dụ trên class component phải quản lý quán nhiều state, trong khi function component thì chỉ cần giá trị của biến đầu ra. Mã ngắn hơn chưa chắc là dễ đọc hơn nhưng chắc chắn việc quản lý state sẽ dễ dàng hơn. Và thường thì các đoạn mã trong function component của chúng tôi ngắn hơn trong class component là từ 46% đến 50%
Lời kết
Như vậy là mình cùng các bạn đã đi qua vòng đời của React để biết sự khác biệt của class component và function component, cũng như tìm hiểu useMemo để quản lý state trong React. Hy vọng các bạn sẽ có thêm những kiến thức giúp tối ưu code và maintain dễ dàng hơn về sau. Chúc các bạn có 1 ngày làm việc hiệu quả.