Giới thiệu
Đầu tiên thì ứng dụng emoji search là một trong các ví dụ cơ bản của reactjs, bạn có thể xem ở đây
https://github.com/ahfarmer/emoji-search
Bài viết này mình đã viết lại ứng dụng này, cùng với áp dụng Recoiljs trong việc quản lý state
Ứng dụng Emoji Search
Đầu tiên để quản lý state với Recoil, mình sẽ phải tạo các atoms
src/Atoms/searchInput.js
1 2 3 4 5 6 7 8 | <span class="token keyword">import</span> <span class="token punctuation">{</span> atom <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"recoil"</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">atom</span><span class="token punctuation">(</span><span class="token punctuation">{</span> key<span class="token punctuation">:</span> <span class="token string">"searchInputState"</span><span class="token punctuation">,</span> <span class="token keyword">default</span><span class="token punctuation">:</span> <span class="token string">""</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
src/Atoms/emojiList.js
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">import</span> <span class="token punctuation">{</span> selector <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"recoil"</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> getEmojiList <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"../common"</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">selector</span><span class="token punctuation">(</span><span class="token punctuation">{</span> key<span class="token punctuation">:</span> <span class="token string">"emojiListSelector"</span><span class="token punctuation">,</span> <span class="token keyword">get</span><span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token keyword">get</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> emojiList <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getEmojiList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> emojiList<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> |
Ứng dụng này mình chỉ cần 2 atoms cơ bản, searchInput
dùng khi tìm kiếm emoji, emojiList
để lưu data. Với emojiList
mình muốn dùng hàm atomFamily
, nhưng không hiểu sao lại không dùng được
Sau khi có atom, bạn có thể dùng selector để “chế biến” ra dữ liệu mình cần.
src/Selectors/filteredEmojiList.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">import</span> <span class="token punctuation">{</span> selector <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"recoil"</span><span class="token punctuation">;</span> <span class="token keyword">import</span> searchInputAtom <span class="token keyword">from</span> <span class="token string">"../Atoms/searchInput"</span><span class="token punctuation">;</span> <span class="token keyword">import</span> emojiListAtom <span class="token keyword">from</span> <span class="token string">"../Atoms/emojiList"</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> filterEmoji <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"../common"</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">selector</span><span class="token punctuation">(</span><span class="token punctuation">{</span> key<span class="token punctuation">:</span> <span class="token string">"filteredEmojiListSelector"</span><span class="token punctuation">,</span> <span class="token keyword">get</span><span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token keyword">get</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> searchText <span class="token operator">=</span> <span class="token keyword">get</span><span class="token punctuation">(</span>searchInputAtom<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> emojiList <span class="token operator">=</span> <span class="token keyword">get</span><span class="token punctuation">(</span>emojiListAtom<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">filterEmoji</span><span class="token punctuation">(</span>emojiList<span class="token punctuation">,</span> searchText<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> |
src/common.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getEmojiList</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"/emojiList.json"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> response<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 keyword">export</span> <span class="token keyword">function</span> <span class="token function">filterEmoji</span><span class="token punctuation">(</span>emojiList<span class="token punctuation">,</span> text<span class="token punctuation">,</span> maxResult <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> emojiList <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>emoji <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>emoji<span class="token punctuation">.</span>title<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>text<span class="token punctuation">.</span><span class="token function">toLowerCase</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">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>emoji<span class="token punctuation">.</span>keywords<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>text<span class="token punctuation">.</span><span class="token function">toLowerCase</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">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</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 punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> maxResult<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Giờ mình chỉ cần render dữ liệu ra UI nữa thôi.
src/App.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> <span class="token punctuation">{</span> RecoilRoot <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"recoil"</span><span class="token punctuation">;</span> <span class="token keyword">import</span> Emoji <span class="token keyword">from</span> <span class="token string">"./Components/Emoji"</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">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">return</span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>RecoilRoot</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>React.Suspense</span> <span class="token attr-name">fallback</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</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 plain-text">Loading...</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><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Emoji</span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>React.Suspense</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>RecoilRoot</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
RecoilRoot
sẽ tạo một context để chúng ta có thể sử dụng các hàm khác của Recoil
. Bạn nên đặt nó bao ngoài App. React.Suspense
đây là một tính năng vẫn đang phát triển của React, nhưng khi bạn sử dụng Recoil thì nó sẽ yêu cầu bạn sử dụng tính năng này, khi mà bạn fetch dữ liệu có thể hiển thị ra component thay thế.
Bên dưới là component Emoji, để dễ theo dõi, nên mình viết cả các components khác trong cùng một file này.
src/Components/Emoji.js
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 48 49 50 51 52 53 54 55 56 57 58 | <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 punctuation">{</span> useRecoilValue<span class="token punctuation">,</span> useRecoilState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"recoil"</span><span class="token punctuation">;</span> <span class="token keyword">import</span> filteredEmojiList <span class="token keyword">from</span> <span class="token string">"../Selectors/filteredEmojiList"</span><span class="token punctuation">;</span> <span class="token keyword">import</span> searchInput <span class="token keyword">from</span> <span class="token string">"../Atoms/searchInput"</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">Header</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 keyword">const</span> <span class="token punctuation">[</span>text<span class="token punctuation">,</span> <span class="token keyword">set</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useRecoilState</span><span class="token punctuation">(</span>searchInput<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">handleChange</span> <span class="token operator">=</span> e <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">set</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 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>Header<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>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>smile, angry,...<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>text<span class="token punctuation">}</span></span> <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>handleChange<span class="token punctuation">}</span></span> <span class="token attr-name">autoFocus</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><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">EmojiCard</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> title<span class="token punctuation">,</span> symbol <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> codePointHex <span class="token operator">=</span> symbol<span class="token punctuation">.</span><span class="token function">codePointAt</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 function">toString</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> src <span class="token operator">=</span> <span class="token template-string"><span class="token string">`//cdn.jsdelivr.net/emojione/assets/png/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>codePointHex<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.png`</span></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>EmojiCard<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>img</span> <span class="token attr-name">alt</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>title<span class="token punctuation">}</span></span> <span class="token attr-name">src</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>src<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>span</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>title<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</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><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">EmojiList</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> emojiList <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> nodes <span class="token operator">=</span> emojiList<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>emoji<span class="token punctuation">,</span> index<span class="token punctuation">)</span> <span class="token operator">=></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>EmojiCard</span> <span class="token spread"><span class="token punctuation">{</span><span class="token punctuation">...</span><span class="token attr-value">emoji</span><span class="token punctuation">}</span></span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>index<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></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 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>EmojiList<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>nodes<span class="token punctuation">}</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> <span class="token keyword">const</span> <span class="token function-variable function">Emoji</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 keyword">const</span> emojiList <span class="token operator">=</span> <span class="token function">useRecoilValue</span><span class="token punctuation">(</span>filteredEmojiList<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>Emoji<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>Header</span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>EmojiList</span> <span class="token attr-name">emojiList</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>emojiList<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>div</span><span class="token punctuation">></span></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> Emoji<span class="token punctuation">;</span> |
Các bạn có thể thấy các hooks của Recoil: useRecoilValue
và useRecoilState
. Đây đều là các hooks cơ bản, bạn có thể tham khảo thêm tại đây.
https://recoiljs.org/docs/api-reference/core/useRecoilState
Kết quả
Bạn có thể xem bản demo và mã hoàn chỉnh của ứng dụng ở đây:
https://codesandbox.io/s/admiring-albattani-wm1f9
Mình khá là thích thư viện này vì việc dễ dàng chia sẻ state, cú pháp cũng rất ngắn gọn dễ hiểu.
Có thể mình sẽ sử dụng nó từ giờ để thay thế cho việc sử dụng React Context.