Bài viết này chia sẻ cách mình tạo một ứng dụng ToDo đơn giản với FeathersJS ở phía server và ReactJs ở Client. Mình sẽ đi vào một số phần chính là cách cài đặt, và sử dụng FeathersJS client với ReactJS như thế nào mà thôi, nên bài viết sẽ bỏ qua khá nhiều phần như xác thực dữ liệu, hiệu ứng, …
Cài đặt
Tạo thư mục cho dư án:
1 2 3 4 5 | mkdir todo cd todo && mkdir server cd server feathers generate app |
Tạo service Tasks:
1 2 | feathers generate service |
Sửa file src/models/tasks.model.js
:
1 2 3 4 5 6 7 8 | <span class="token keyword">const</span> schema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Schema</span><span class="token punctuation">(</span><span class="token punctuation">{</span> text<span class="token punctuation">:</span> <span class="token punctuation">{</span> type<span class="token punctuation">:</span> String<span class="token punctuation">,</span> required<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> status<span class="token punctuation">:</span> <span class="token punctuation">{</span> type<span class="token punctuation">:</span> String<span class="token punctuation">,</span> <span class="token keyword">enum</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'unfinished'</span><span class="token punctuation">,</span> <span class="token string">'finished'</span><span class="token punctuation">,</span> <span class="token string">'removed'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">default</span><span class="token punctuation">:</span> <span class="token string">'unfinished'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> user<span class="token punctuation">:</span> <span class="token punctuation">{</span> type<span class="token punctuation">:</span> String<span class="token punctuation">,</span> required<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> ref<span class="token punctuation">:</span> <span class="token string">'users'</span><span class="token punctuation">,</span> index<span class="token punctuation">:</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 punctuation">{</span> timestamps<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Vậy là xong server rồi đấy
Tiếp theo với Client, trong todo folder:
1 2 3 4 | npx create-react-app client cd client yarn add @feathersjs/feathers @feathersjs/rest-client @feathersjs/authentication-client |
cài đặt Feathers:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token comment">// src/feathers.js</span> <span class="token keyword">import</span> feathers <span class="token keyword">from</span> <span class="token string">'@feathersjs/feathers'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> rest <span class="token keyword">from</span> <span class="token string">'@feathersjs/rest-client'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> authentication <span class="token keyword">from</span> <span class="token string">'@feathersjs/authentication-client'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">feathers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> restClient <span class="token operator">=</span> <span class="token function">rest</span><span class="token punctuation">(</span><span class="token string">'http://localhost:3030'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">configure</span><span class="token punctuation">(</span>restClient<span class="token punctuation">.</span><span class="token function">fetch</span><span class="token punctuation">(</span>window<span class="token punctuation">.</span>fetch<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">configure</span><span class="token punctuation">(</span><span class="token function">authentication</span><span class="token punctuation">(</span><span class="token punctuation">{</span> storageKey<span class="token punctuation">:</span> <span class="token string">'authentication'</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> app<span class="token punctuation">;</span> |
Xong phần cài đặt để có thể sử dụng Feathers trên client, giờ mình có thể bắt tay vào code React rồi
Giao diện mẫu
Viết Code
Cài đặt thêm một số thư viện:
- react-router-dom
- material-ui
- moment
Đăng nhập và đăng ký
Đăng ký:
1 2 3 4 5 6 7 8 9 | <span class="token keyword">const</span> handleSubmitForm <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">await</span> feathers<span class="token punctuation">.</span><span class="token function">service</span><span class="token punctuation">(</span><span class="token string">'users'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">SignUpSuccess</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">SignUpSuccess</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> history<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token string">'/signin'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Đoạn code bên dưới để xử lý phần đăng nhập:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">const</span> handleSubmitForm <span class="token operator">=</span> <span class="token keyword">async</span> data <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> feathers<span class="token punctuation">.</span><span class="token function">authenticate</span><span class="token punctuation">(</span><span class="token punctuation">{</span> strategy<span class="token punctuation">:</span> <span class="token string">'local'</span><span class="token punctuation">,</span> <span class="token operator">...</span>data <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">signInSuccess</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">{</span> <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">'Sign In Failed!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setCurrentUser</span><span class="token punctuation">(</span>undefined<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">const</span> <span class="token function-variable function">signInSuccess</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> user <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setCurrentUser</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span> history<span class="token punctuation">.</span><span class="token function">push</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><span class="token punctuation">;</span> |
Đăng nhập khi đã có accessToken trong localStorage:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <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> reAuthenticate <span class="token operator">=</span> <span class="token keyword">async</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> response <span class="token operator">=</span> <span class="token keyword">await</span> feathers<span class="token punctuation">.</span><span class="token function">reAuthenticate</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>response <span class="token operator">&&</span> response<span class="token punctuation">.</span>user<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setCurrentUser</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>user<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> authenticate <span class="token operator">=</span> <span class="token keyword">async</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> accessToken <span class="token operator">=</span> <span class="token keyword">await</span> feathers<span class="token punctuation">.</span>authentication<span class="token punctuation">.</span><span class="token function">getAccessToken</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>accessToken<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">await</span> <span class="token function">reAuthenticate</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">setLoading</span><span class="token punctuation">(</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">authenticate</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 punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Tổng hợp lại thành cái sơ đồ cho mọi người xem:
Tasks
Lấy toàn bộ task:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <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> loadTasks <span class="token operator">=</span> <span class="token keyword">async</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> response <span class="token operator">=</span> <span class="token keyword">await</span> feathers<span class="token punctuation">.</span><span class="token function">service</span><span class="token punctuation">(</span><span class="token string">'tasks'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">{</span> query<span class="token punctuation">:</span> <span class="token punctuation">{</span> user<span class="token punctuation">:</span> user<span class="token punctuation">.</span>_id<span class="token punctuation">,</span> $sort<span class="token punctuation">:</span> <span class="token punctuation">{</span> status<span class="token punctuation">:</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> updatedAt<span class="token punctuation">:</span> <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 punctuation">;</span> <span class="token function">setTasks</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token function">loadTasks</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>task<span class="token punctuation">,</span> user<span class="token punctuation">.</span>_id<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Đoạn code [task, user._id]
này nghĩa là khi task thay đổi thì mình sẽ load lại toàn bộ danh sách task, mình sử dụng cách này để lấy lại danh sách task khi thêm, và sửa trạng thái task.
Thêm task mới:
1 2 3 4 5 6 7 | <span class="token keyword">const</span> addTask <span class="token operator">=</span> <span class="token keyword">async</span> data <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> feathers <span class="token punctuation">.</span><span class="token function">service</span><span class="token punctuation">(</span><span class="token string">'tasks'</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span> user<span class="token punctuation">:</span> user<span class="token punctuation">.</span>_id<span class="token punctuation">,</span> <span class="token operator">...</span>data <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setTask</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Thay đổi trạng thái:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">const</span> <span class="token function-variable function">getTask</span> <span class="token operator">=</span> <span class="token punctuation">(</span>id<span class="token punctuation">,</span> tasks<span class="token punctuation">)</span> <span class="token operator">=></span> tasks<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span>task <span class="token operator">=></span> id <span class="token operator">===</span> task<span class="token punctuation">.</span>_id<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> changeStatusTask <span class="token operator">=</span> <span class="token keyword">async</span> id <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> task <span class="token operator">=</span> <span class="token function">getTask</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> tasks<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> status <span class="token operator">=</span> task<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token string">'finished'</span> <span class="token operator">?</span> <span class="token string">'unfinished'</span> <span class="token punctuation">:</span> <span class="token string">'finished'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> feathers <span class="token punctuation">.</span><span class="token function">service</span><span class="token punctuation">(</span><span class="token string">'tasks'</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">patch</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> <span class="token punctuation">{</span> status <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setTask</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Kết quả
Repo github: https://github.com/hungkieu/Feathers-React-Todo-App
Mình tóm tắt bài viết với các ý chính như sau:
- Sử dụng Feathers Client với ReactJS.
- Xác thực người dùng.
- Thêm, sửa, xóa.
Cảm ơn vì đã đọc bài viết của mình