React 練習 - 便條紙

更新於 發佈於 閱讀時間約 9 分鐘

最近正在學 React,看見課程中要做出一個類似便條紙的頁面,老師說靈感來自 Google Keep,紀錄自己的想法後丟上去。

google keep頁面

google keep頁面

課程裡做出來的:

post-it 頁面

post-it 頁面


主要是練習 React 中的 props 跟怎麼使用狀態,以下是寫出來的 component 之間的關係。

<App>
<Header />
<CreateNote />
<Note />
<Footer />
<App />

使用 useState 將表單元素轉成可控制元件

首先是新增便條紙 <CreateNote /> 的部分,要將不可控的元素轉成可控制元素(controlled elements),因為表單元素是由 DOM 管理輸入的狀態,​轉成受控元素就能允許 React 保持對輸入的控制。

步驟:

  • 使用 useState 來初始化狀態
const [note, setNote] = useState({ title: '', content: '' })
  • 建立處理事件的函數
function handleChange(e) {
const { name, value } = e.target
setNote((note) => {
return { ...note, [name]: value }
})
}
  • 將狀態綁定與函數在元素
<input
// ...
onChange={handleChange}
value={note.title}/>
<textarea
// ...
onChange={handleChange}
value={note.content}/>

這時候元素的輸入值就可以預測(因為是我們在控制的),也能進行動態更新。

提升狀態到父元件 Lift states up

使用者在新增便條紙後,接收值的元件是 <Note /> ,它不是 <CreateNote /> 的子元件。

由於 React 中資料只能由上往下流動,當父元件或其他兄弟元件需要使用同個狀態時,必須將狀態提升到上一層的公共父元件,在這裡就是 <App />

步驟:

  • 提升狀態到 App 元件
const [notes, setNotes] = useState([])

function handleAddNote(newNote) {
setNotes((notes) => [...prevNotes, newNote])
}

function handleDeleteNote(id) {
setNotes((notes) => notes.filter((note, i) => i !== id))
}
  • 用 props 傳遞給 CreateNoteNote 元件
<CreateNote onAddNote={handleAddNote} />
注意 notes 是陣列 要用 map 迭代資料給 Note
{notes.map((note, i) => {
return (
<Note
key={i}
id={i}
title={note.title}
content={note.content}
onDeleteNote={handleDeleteNote}/>
)
})}
  • 使用 props
function CreateNote({ onAddNote }) {
// ...
function handleClick(e) {
e.preventDefault()
onAddNote(note)
}
return (
// ...
<button onClick={handleClick}>Add</button>
)
}
function Note({ id, title, content, onDeleteNote }) {
return (
<div className="note">
<h2>{title}</h2>
<p>{content}</p>
<button onClick={() => onDeleteNote(id)}>DELETE</button>
</div>
)
}

現在新增便條紙後,發現就算沒有寫任何內容還是可以新增,所以回到 CreateNote 加上一個預防動作:

function handleClick(e) {
e.preventDefault()
// 新增以下這行
if (note.title === '' || note.content === '') return
// 當沒有內容時 就不再執行​
onAddNote(note)
}

使用 useEffect 將資料存在 localStorage

新增完便條紙後,如果重新整理網頁,狀態就會回到初始狀態,也就是空的陣列:

const [notes, setNotes] = useState([]) // 初始狀態為空陣列

於是就想到可以把資料存在 localStorage,讓使用者下次打開頁面時,可以再將儲存的資料渲染出來。

步驟:

  • 將上面那段改寫
const [notes, setNotes] = useState(function () {
const storageValue = localStorage.getItem('notes')
return storageValue ? JSON.parse(storageValue) : []
// 如果 localStorage 沒有資料 返回空陣列
})
  • 新增 useEffect
useEffect(
function () {
localStorage.setItem('notes', JSON.stringify(notes))
},
[notes]
// 當 notes 更新時 執行上面的函數
)

完成!

為了提高重用度 改寫成 Custom Hook

很多時候,將資料儲存到 localStorage 是常有的事,如果可以把上面那一大段程式碼寫成自定義的 Hook,未來其他專案要使用時,也能輕鬆移植過去。

最後就是要來寫 Custom Hook:

  • 程式碼寫在另一個檔案 使用 use 命名 Custom Hook
export function useLocalStorageState(initialValue, key) {}
  • 將剛剛寫的 useState、useEffect 剪下貼過來
export function useLocalStorageState(initialValue, key) {
const [value, setValue] = useState(function () {
const storageValue = localStorage.getItem(key)
return storageValue ? JSON.parse(storageValue) : initialValue
})

useEffect(
function () {
localStorage.setItem(key, JSON.stringify(value))
},
[value, key]
)
}
  • 返回陣列或物件
export function useLocalStorageState(initialValue, key) {
const [value, setValue] = useState(function () {
const storageValue = localStorage.getItem(key)
return storageValue ? JSON.parse(storageValue) : initialValue
})

useEffect(
function () {
localStorage.setItem(key, JSON.stringify(value))
},
[value, key]
)

return [value, setValue] // 返回陣列
}
  • 在元件中導入 Custom Hook
const [notes, setNotes] = useLocalStorageState([], 'notes')

這樣就大功告成啦!

重新整理也不會遺失寫好的便條紙

重新整理也不會遺失寫好的便條紙



練習 repo 頁面 ▶ Post-it - build with React

留言
avatar-img
留言分享你的想法!
avatar-img
碎碎念
5會員
14內容數
本來是理科生,在被物理放棄之後成為了文科生,有時理性思考,偶爾卻會脫口出感性的字句;喜歡打字的聲音,以生活為靈感寫下過去、現在與未來。
碎碎念的其他內容
2024/07/11
又是 Frontend Mentor 的挑戰! 這次要寫一個網站,讓使用者可以查詢國家,並且顯示國旗、人口、首都、語言、幣別等資料。原始檔案提供了 JSON 檔,或是可以去串接 REST Countries API,動態取得並呈現各國的資料,基本上看我們要怎麼寫都可以。
Thumbnail
2024/07/11
又是 Frontend Mentor 的挑戰! 這次要寫一個網站,讓使用者可以查詢國家,並且顯示國旗、人口、首都、語言、幣別等資料。原始檔案提供了 JSON 檔,或是可以去串接 REST Countries API,動態取得並呈現各國的資料,基本上看我們要怎麼寫都可以。
Thumbnail
2024/07/04
這次寫的是 Frontend Mentor 挑戰的太空旅遊介紹的網站,當初作了一個星期,首頁都沒辦法完成,那時候對 flexbox、grid 的樣式寫法超不熟,用 position 也是作的很落漆。直到最近才又重新嘗試寫了一次...
Thumbnail
2024/07/04
這次寫的是 Frontend Mentor 挑戰的太空旅遊介紹的網站,當初作了一個星期,首頁都沒辦法完成,那時候對 flexbox、grid 的樣式寫法超不熟,用 position 也是作的很落漆。直到最近才又重新嘗試寫了一次...
Thumbnail
2024/06/22
從上次作完喵喵健身教練網頁後,覺得自己以前作的個人簡歷網站實在是太醜了,而且當初為了搶快,直接套用 Bootstrap 版型,根本不用寫 JavaScript(燦笑) 所以這次決定把網站重寫,就算是從 1 開始吧,1-10 分作到 8 分就好,不然完美主義真的會搞死自己,那道光很快就來了(?)
Thumbnail
2024/06/22
從上次作完喵喵健身教練網頁後,覺得自己以前作的個人簡歷網站實在是太醜了,而且當初為了搶快,直接套用 Bootstrap 版型,根本不用寫 JavaScript(燦笑) 所以這次決定把網站重寫,就算是從 1 開始吧,1-10 分作到 8 分就好,不然完美主義真的會搞死自己,那道光很快就來了(?)
Thumbnail
看更多
你可能也想看
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
全球科技產業的焦點,AKA 全村的希望 NVIDIA,於五月底正式發布了他們在今年 2025 第一季的財報 (輝達內部財務年度為 2026 Q1,實際日曆期間為今年二到四月),交出了打敗了市場預期的成績單。然而,在銷售持續高速成長的同時,川普政府加大對於中國的晶片管制......
Thumbnail
全球科技產業的焦點,AKA 全村的希望 NVIDIA,於五月底正式發布了他們在今年 2025 第一季的財報 (輝達內部財務年度為 2026 Q1,實際日曆期間為今年二到四月),交出了打敗了市場預期的成績單。然而,在銷售持續高速成長的同時,川普政府加大對於中國的晶片管制......
Thumbnail
重點摘要: 6 月繼續維持基準利率不變,強調維持高利率主因為關稅 點陣圖表現略為鷹派,收斂 2026、2027 年降息預期 SEP 連續 2 季下修 GDP、上修通膨預測值 --- 1.繼續維持利率不變,強調需要維持高利率是因為關稅: 聯準會 (Fed) 召開 6 月利率會議
Thumbnail
重點摘要: 6 月繼續維持基準利率不變,強調維持高利率主因為關稅 點陣圖表現略為鷹派,收斂 2026、2027 年降息預期 SEP 連續 2 季下修 GDP、上修通膨預測值 --- 1.繼續維持利率不變,強調需要維持高利率是因為關稅: 聯準會 (Fed) 召開 6 月利率會議
Thumbnail
看到「清空練習」四個字時,你會想到甚麼?斷捨離?丟東西?物品收納?還是只是單純地做用品的分類而已?關於整理,我想說的其實是「整理,是一種習慣」,培養這個習慣的契機點
Thumbnail
看到「清空練習」四個字時,你會想到甚麼?斷捨離?丟東西?物品收納?還是只是單純地做用品的分類而已?關於整理,我想說的其實是「整理,是一種習慣」,培養這個習慣的契機點
Thumbnail
這篇文章描述了作者對於生活中遺留的習慣、記憶和物品的反思和回憶。作者選擇以網路書寫的方式來重新梳理和分享這些記憶。從筆記的撕下到數位工具的使用,都展現了作者對於記憶整理的執著和努力。
Thumbnail
這篇文章描述了作者對於生活中遺留的習慣、記憶和物品的反思和回憶。作者選擇以網路書寫的方式來重新梳理和分享這些記憶。從筆記的撕下到數位工具的使用,都展現了作者對於記憶整理的執著和努力。
Thumbnail
★檢視自己與物品之間的關係,進而規劃美好未來的願景。
Thumbnail
★檢視自己與物品之間的關係,進而規劃美好未來的願景。
Thumbnail
這篇文章對於如何進行斷捨離、整理收納的過程進行了深入的探討,並分享了個人的經歷和體悟。通過整理自己的物品,作者找到了新領悟。這篇文章是一篇感性的分享,能夠啟發讀者開始自己的整理收納旅程。
Thumbnail
這篇文章對於如何進行斷捨離、整理收納的過程進行了深入的探討,並分享了個人的經歷和體悟。通過整理自己的物品,作者找到了新領悟。這篇文章是一篇感性的分享,能夠啟發讀者開始自己的整理收納旅程。
Thumbnail
去年3月搬完家之後,除了想跟想環境熟悉之外,我也想重新認識自己。於是我開始著手整理在疫情期間,隨手寫的童年,回當時的IG限動找感覺。每天起床先對著電腦冥想2小時,再出門工作⋯ 忘記這樣的日子多久了,我開始不再是對著電腦冥想,而是開始打字,打打刪刪⋯電腦的垃圾桶堆滿了我的胡言亂語。 七個月之後,我
Thumbnail
去年3月搬完家之後,除了想跟想環境熟悉之外,我也想重新認識自己。於是我開始著手整理在疫情期間,隨手寫的童年,回當時的IG限動找感覺。每天起床先對著電腦冥想2小時,再出門工作⋯ 忘記這樣的日子多久了,我開始不再是對著電腦冥想,而是開始打字,打打刪刪⋯電腦的垃圾桶堆滿了我的胡言亂語。 七個月之後,我
Thumbnail
使用 React 做出一個便條紙的頁面,可以紀錄自己的想法後丟上去。並且讓使用者即使重新整理頁面,也不會遺失資料。
Thumbnail
使用 React 做出一個便條紙的頁面,可以紀錄自己的想法後丟上去。並且讓使用者即使重新整理頁面,也不會遺失資料。
Thumbnail
從去年4月開始到現在,準備邁入斷捨離練習第12個月了!回顧幾年前實施過短暫斷捨離的自己,因為重新閱讀這本「一日丟一物的簡單生活提案」進而下定決心開始認真斷捨離。 現在已經養成一個習慣,只要看到要斷捨離的物品,就會拍照下來上傳IG寫紀錄。原先只是單純紀錄幾行文字,到後來使用固定書寫模式紀錄斷捨離的各
Thumbnail
從去年4月開始到現在,準備邁入斷捨離練習第12個月了!回顧幾年前實施過短暫斷捨離的自己,因為重新閱讀這本「一日丟一物的簡單生活提案」進而下定決心開始認真斷捨離。 現在已經養成一個習慣,只要看到要斷捨離的物品,就會拍照下來上傳IG寫紀錄。原先只是單純紀錄幾行文字,到後來使用固定書寫模式紀錄斷捨離的各
Thumbnail
做生涯規劃、選擇,就像買一張刮刮樂;買了再多張,若沒有試過、刮開來看,永遠不知道究竟是廢紙一張,還是千萬頭獎。
Thumbnail
做生涯規劃、選擇,就像買一張刮刮樂;買了再多張,若沒有試過、刮開來看,永遠不知道究竟是廢紙一張,還是千萬頭獎。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News