Make a Calendar React hook
使用 date-fns 開發 calendar hook
Moment vs date-fns
一般來說,只要有關時間的 lib 我們都會想到 moment,我一開始也是使用moment,但發現 moment 不太適合用在 react hook 上,主要原因是在於,moment 是mutable,假設把moment放到 useState 管理會發現,hook 不會刷新,但是 moment 的狀態會更新。
而我實際操作後,也參考了 moment vs date-fns 該文作者歸納出兩個 date-fns 比moment 好的優點:
1. immutable
2. size more smaller
所以我選擇用 date-fns 來設計。
Moment Demo
從這邊可以發現,當直接操作 moment,不用 setState 就可以更新 moment 。
如果想了解更好的設計方法可參考:
useCalendar
我原先有使用原生的 new Date 設計日曆,可參考這篇 MensWalk - Pure React Calendar
但是如果仔細看會發現,不夠乾淨,太過混雜,於是我重新改寫一遍。
切換月份
使用 date-fns 後切換月份就變得更加精簡及嚴謹,而且 pure 寫法因為是直接做數字加減所以還會有浮點數的問題。
pure
const [today, setToday] = useState(new Date()); const [currentMonth, setCurrentMonth] = useState(today.getMonth()); const [currentYear, setCurrentYear] = useState(today.getFullYear()); const setNextMonth = () => { if (currentMonth === 11) { setCurrentMonth(0); setCurrentYear(currentYear + 1); } else { setCurrentMonth(currentMonth + 1); } }; const setPreMonth = () => { if (currentMonth === 0) { setCurrentYear(currentYear - 1); setCurrentMonth(11); } else { setCurrentMonth(currentMonth - 1); } };
date-fns
const [today, setToday] = useState(new Date()); const setNextMonth = () => { setToday(addMonths(today, 1)) } const setPreMonth = () => { setToday(subMonths(today, 1)) }
顯示當月日期
日期回傳 二維陣列,使用 date-fns 處理上更加好懂,我這邊還增加了 上個月 及 下個月的日期也一起放進來,這樣日期顯示上更加完整。
選擇日期
我預設日曆的資料格式為下,往後如果需要新增日期的操作可以更加彈性的新增。
let dateInfo = { otherMonth: false, date: null, }
測試
測試比較特別的點是在於,怎麼精準的測出當月的日曆,這邊我們只要抓,當天的星期及週數,帶入到 hooks 的 days 中比對是否為同一天即可,這樣我們就不用測是不是整個陣列格式都一樣了。
Conclusion
改寫後的 useCalendar 更加完整,程式邏輯更加清楚,往後想要做進一步修正更加容易,如果想再 React 操作 時間相關的應用,date-fns 會是更好的選擇。
Demo
https://wildfrontend.github.io/useCalendar/?path=/story/calendar-test--demo
Source