# 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"
/>
</>
)
}
```
**就會看到畫面啦~**

---
## 取時間
取時間有兩種常見方式
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格式進行比較

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 判斷傳入值,以防錯誤