# React情緒日記 side project > 感謝最強助手gpt幫我想到一個有趣的點子 ## 主要想完成的功能 讓使用者透過每天寫日記關心自己的內心, 並且透過點月曆日期讓使用者可以快速查閱當天的日記 回憶當時的情境。 ## 環境建置 > 本專案使用vite+React製作 使用vite建立檔案`npm create vite@latest` 選擇React+js 在按照指示操作,出現畫面就完成! ## 加入月曆套件 加載套件`npm install react-calendar` 加入專案 ``` import Calendar from 'react-calendar' function App() { const [value,setValue]=useState(new Date()) return ( <> <Calendar onChange={setValue} value={value} locale="zh-TW" /> </> ) } ``` **就會看到畫面啦~** ![螢幕擷取畫面 2025-04-15 173206](https://hackmd.io/_uploads/rJRoPso0kg.png) --- ## 取時間 取時間有兩種常見方式 1. `toISOString().slice(0, 10)` 這個會取到 UTC時間的yyyy/mm/dd,.slice(0,10)是從第0字元(含)取到第10字元(不含),也就是取出第0~9共10個字元,但因為月曆會有時區問題,必須取當地時間才不會錯位,所以要改用第二種方法 1. ``` function formatDate(date) { if (!(date instanceof Date)) { date = new Date(date); // 嘗試修正為 Date 物件 } // 將 Date 轉換為 yyyy-mm-dd,不會受到時區影響 const year = date.getFullYear(); const month = `${date.getMonth() + 1}`.padStart(2, '0'); const day = `${date.getDate()}`.padStart(2, '0'); return `${year}-${month}-${day}` } ``` > 因為後續經常需要用這個函式做比較,所以在前面加上instanceof Date確保格式為Date > 透過.getFullYear,.getMonth,.getDate的方式取得當地時間的年月日,要特別注意的是getMonnth從0開始所以要記得+1,還有為了要對齊數值,所以要加入.padStart(2, '0'),若不足2位數前方補0 ### 按下日期顯示當天的日記 ``` const selectedEntry = diaryEntries.find( (entry) => entry.date === formatDate(value) ); ``` 透過.find找出與點擊的日期相同的日記 ``` {selectedEntry ? ( <div style={{ marginTop: '2rem', padding: '1rem', border: '1px solid #ccc', borderRadius: '8px' }}> <h2>{selectedEntry.date}</h2> <p>情緒:{selectedEntry.emotions.join(' ')}</p> <p>{selectedEntry.content}</p> </div> ) : ( <p style={{ marginTop: '2rem' }}>這天還沒有日記喔~</p> )} ``` 再透過三元運算子切換要顯示的內容,之後也可以單獨抽出元件使用 (但原則是先把功能寫出來再抽元件,方便除錯) --- ## 在月視圖加上emoji(react-calendar) ``` <Calendar tileContent={({ date, view }) => { if (view !== 'month') return null; // 只在月視圖顯示 // 將 diaryEntries 中的日期字串轉成 Date,並比對是否為當前格子的日期 const entry = diaryEntries.find( (e) => formatDate(new Date (e.date)) === formatDate(date) ); // 若有日記資料,顯示當日的情緒 icon return entry ? ( <div style={{ fontSize: '0.8rem', marginTop: 4 }}> //因為emoji可多選,因此用.map顯示整個陣列 {entry.emotions.map((e, idx) => ( <span key={idx}>{e}</span> ))} </div> ) : null; }} /> ``` 透過.find找出與日期相同的日記,並將emoji取出並顯示 要特別注意e.date原本是字串 要加上new Date()才能換成Date格式進行比較 ![螢幕擷取畫面 2025-04-16 112622](https://hackmd.io/_uploads/rJVOXohCJe.png) icon就會像這樣出現在月曆上 --- ## 設置路由 > 為了避免將所有程式碼都擠在 App.jsx,先建立多頁式路由結構,將每一頁拆成獨立元件管理 ### step1. 製作元件 將每一頁的內容分開做成獨立元件 例如: `pages/CalendarPage.jsx` `pages/NewDiary.jsx` ### step2. 在pages資料夾下建立index.jsx 統一export pages資料夾下的元件 ### step3. 寫路由 在根目錄下建立routes>index.jsx ``` //匯入元件 import App from "../App"; import { CalendarPage, NewDiary } from "../pages/index" import { createHashRouter,createBrowserRouter } from "react-router-dom" //寫路由表 const routes = [ { path: "/", element: <App />, children: [{ index: true, element: <CalendarPage /> }] }, { path:"newDiary", element:<NewDiary/> } ] // 根據開發環境判斷使用哪種 Router const router = import.meta.env.MODE === "development" ? createBrowserRouter(routes) : createHashRouter(routes); export default router ``` ### step4. 修改main.jsx 將原本的ReactDOM.createRoot(...).render(<App />)改為 **` <RouterProvider router={router}></RouterProvider> `** ### step5. 修改App.jsx 將內容清除並加入 **`<Outlet/>`** --- ## 透過按鈕切換路由 > 在月曆頁中,如果該天尚未有日記,可以顯示一個按鈕,點擊後前往新增日記畫面並帶入所選日期 ### step1. 在`CalendarPage`引入`useNavigate` ``` import { useNavigate } from 'react-router-dom'; const navigate =useNavigate() ``` ### step2. 加入按鈕導向 `/newDiary?date=xxx` ``` <button onClick={()=>navigate(newDiary?date=${newEntryDate})}> 出發寫日記</button> ``` ${newEntryDate}是為了帶入點選的日期 ### step3. 在NewDiary設定newEntryDate ``` const defaultDate = searchParams.get('date')|| formatDate(new Date()) const [newEntryDate, setNewEntryDate] = useState(formatDate(defaultDate)); ``` > ⚠️ 建議 formatDate() 裡使用 instanceof Date 判斷傳入值,以防錯誤