0%

React-To-Do-List

從零開始學習react框架,

1. create react app

執行命令:npx create-react-app my-app >> cd my-app >> npm start
在執行 npx create-react-app後,就會自動幫你建立出一個類似git repo的folder,

若需要執行react,則需要進入 folder name裡面,使用npm start就可以啟用react prodject

2. about react framework

react framework 裡面有一個 App.js, index.html, index.css /src
主要render的地方是在App.js 將元件建立在/src >> /components
index.html : 不需要改動,可以控制web title, icon等…
index.css : 就跟正常的css一樣。
/component: 個人覺得是react的精髓,可以將所以元件統一管理,統一使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import PropTypes from 'prop-types'

let Button = ({ color, text, onClick }) => {

return (
<button
onClick={onClick}
style={{ backgroundColor: color }}
className='btn'
>
{text}
</button>
)
}

Button.defaultProps = {
color: 'steelblue',
}

Button.propTypes = {
text: PropTypes.string,
color: PropTypes.string,
onClick: PropTypes.func,
}

export default Button

App.js即可以improt button

1
2
import Header from './components/Button';
<Button />

3.當元件都準備好後 就可以在 App.js 組合出畫面摟

1
2
3
4
5
6
7
8
9
10
11
12
13
return (
<Router>
<div className='container'>
<Header />
<Route
<Tasks />
</>
/>
<Route path='/about' component={About} />
<Footer />
</div>
</Router>
)}

可以發現到使用框架建立出來的更易讀,也可以更容易修改,牽一髮動全身。
通常可以用在各種大量使用的地方,像是或是,常常會使用到的地方,在使用react後就更容易大量修改了。

##4. 介紹完簡易的reactUl 那就來看看To Do List吧

首先建立一個Header.js
css color
可以看到在Button 裡面我用了一些三元運算,
color = { ShowAdd ? ‘red’ : ‘green’ } >> 可以解釋成在showAdd === true時是red 反之green
text ={ ShowAdd ? ‘red’ : ‘green’ } >> 可以解釋成在showAdd === true時是Close 反之 Add
可以看到這裡還有一個 onClick={ onAdd } ,接下來會多做說明,這是react拿來綁定事件就像javascript裡面的 onClick() ="fun()"一樣

1
2
3
4
5
6
7
8
9
10
const Header = ({ title, onAdd, showAdd }) => {

return (
<header className="header">
<h1> {title}</h1>
<Button color={showAdd ? 'red': 'green'} text={showAdd ? 'Close' : 'Add'} onClick={ onAdd }/>
</header>
)
}
這是 src/components/Header.js

同時在App.js裡面 綁定onclick event() >>onAdd

1
2
3
4
onAdd={() => setShowAddTask(!showAddTask)}
showAdd={showAddTask}

這是 App.js

onAdd === ture 可以去判斷綁定的事件 setShowAddTask(), showAdd()
在這裡

1
2
setShowAddTask(!showAddTask)}·    >>這裡的! 是指 ture 的時候回傳false
showAdd={showAddTask} >> 這裡是指 ture 的時候回傳true

有興趣的人可以去看看在兩個function 加上! 或是移除!

##5. 對回傳值有點概念之後 讓我們來看看如何使用react的 State吧!!!

react hook useState()

在這裡我是使用reack hook 來將資料儲存,
附上一個簡易的hook sample

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { useState } from 'react';

function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);

return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

Declaring multiple state variables
You can use the State Hook more than once in a single component:

1
2
3
4
5
6
7
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}

這裡說到當我import React, { useState } from 'react'
宣告 const [count, setCount] = useStata(0) 我就可以使用count & setCount

來看看to do list 的相關應用吧!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { useState } from 'react'

const AddTask = ({ onAdd }) => {
const [text, setText] = useState('')
const [day, setDay] = useState('')
const [reminder, setReminder] = useState(false)

const onSubmit = (e) => {
e.preventDefault()
if (!text) {
alert('Please add a task')
return
}
onAdd({ text, day, reminder })

setText('')
setDay('')
setReminder(false)
}

return (
<form className='add-form' onSubmit={ onSubmit }>
<div className="form-control">
<label>Task</label>
<input type="text" placeholder='Add Task' value={text} onChange={(e) =>setText(e.target.value)} />
</div>
<div className="form-control">
<label>Day & Time</label>
<input type="text" placeholder='Add Task' value={day} />
</div>
<div className="form-control">
<label>Set Reminder</label>
<input type="checkbox" checked={ reminder } value={reminder} onChange={(e) =>setReminder(e.currentTarget.checked)}/>
</div>

<input type="submit" value='Save Task' class='btn btn-black'/>
</form>
)
}

export default AddTask


這是AddTask.js

在這裡 我宣告了 [text, setText], [day, setDay], [reminder, setReminder]
並且在onSubmit() 裡面 給他會觸發的event

1
2
3
4
5
onAdd({ text, day, reminder })

setText('') // is string
setDay('') // is string
setReminder(false) // is boolean

當一切都宣告好後,我們就可以開始寫寫 欄位會對應到的 value了
欄位裡面放上

1
value={text},  value={day},  value={reminder}

這樣就可以將value回傳需要判斷的地方,
這裡有一個重點 記得要加上onchange event 才可以在欄位編輯

##6. 最後讓我們來看看 資料如何同步到資料庫吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  // Fetch Tasks
const fetchTasks = async () => {
const res = await fetch('http://localhost:5000/tasks')
const data = await res.json()

console.log(data)
return data
}

// Fetch Task
const fetchTask = async (id) => {
const res = await fetch(`http://localhost:5000/tasks/${id}`)
const data = await res.json()

console.log(data)
return data
}


//Delete Task
const deleteTask = async (id) => {
await fetch(`http://localhost:5000/tasks/${id}`, {
method: 'DELETE'
})

setTasks(tasks.filter((task) => task.id !== id))
}

//Toggle Reminder
const toggleReminder = async(id) => {
const taskToToggle = await fetchTask(id)
const updTask = { ...taskToToggle, reminder: !taskToToggle.reminder }

const res = await fetch(`http://localhost:5000/tasks/${id}`, {
method: 'PUT',
headers: {
'Context-type': 'application/json'
},
body: JSON.stringify(updTask)
})

const data =await res.json()

setTasks(tasks.map((task) => task.id === id ? { ...task, reminder: !task.reminder } : task)
)
}

//Add Task
const addTask = async (task) => {
const res = await fetch('http://localhost:5000/tasks', {
method: 'POST',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(task),
})

const data = await res.json()

setTasks([...tasks, data])

}

首先先來說說json DB

step1: npm install -g json-server
json-server db.json 系統會自動生成一個範例的檔案外加執行 Server,你會發現會自動監聽 port:3000
step2: 然後建立一個json-server db.json
step3: 讓我們來了解一下怎麼用吧

1
2
3
4
5
6
7
8
GET (取得資料) >> 將所有要新增的資料寫成 JSON 格式在下面空白處如下圖(id沒輸入沒關係系統會幫你自動遞增產生)

PUT (更新完整資料) >> 接下來是要放入修改的內容(全部欄位必放),Body >> JSON(application/json),將所有要新增的資料寫成 JSON 格式在下面空白處如下圖,就會回傳你的修改結果囉!
**使用 PUT 要注意的是送出修改內容時要將無修改的資料一樣寫上去不然會被洗掉哦!**

PATCH (更新部分資料) >>再來放入修改的內容(可只放修改的欄位),Body > JSON(application/json) (略同PUT)

DELETE (刪除資料) >> 顧名思義就是拿來把資料刪除用的。。

step4: 使用 json-server --watch db.json --port 5000 將port改為5000 避免跟 react server 重複port 產生error

一切都準備就緒 就讓我們來看看內容吧

這裡我覺得最值得一提得地方應該是

1
2
3
4
5
6
7
8
//Delete Task
const deleteTask = async (id) => {
await fetch(`http://localhost:5000/tasks/${id}`, {
method: 'DELETE'
})

setTasks(tasks.filter((task) => task.id !== id))
}

setTasks(tasks.filter((task) => task.id !== id)) filter掉 id !==id
如果寫成將filter(task) => task.id === id 的話 就會發生 下一次的第一筆id 是index=0
但實際上要刪除的是上一次的index=1
當重複很多次之後,就會發生需刪除id 與index對不上。

最後的最後,來分享一下踩到的地雷跟心得吧

在學習的過程,發生了一個怎麼找都找不到解答的問題。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
這是Footer.js

const Footer = () => {
return (
<div>
<Footer>Copyright &copy; 2021</Footer>
<a href="/about">About</a>
</div>
)
}

export default Footer

____________________以下是APP.js______________________________________
<Router>
<div className='container'>
<Header
onAdd={() => setShowAddTask(!showAddTask)}
showAdd={!showAddTask}
/>
<Route
path='/' exact render={(props) => (
<>
{showAddTask && <AddTask onAdd={addTask} />}
{tasks.length > 0 ? (
<Tasks
tasks={tasks}
onDelete={deleteTask}
onToggle={toggleReminder}
/>
) : (
'No Tasks To Show'
)}
</>
)}
/>
<Route path='/about' component={About} />
<Footer />
</div>
</Router>

issue: 當npm start時 會做出一個web, 但到最後都會發生網頁無回應。
看到這裡大家應該跟我想的一樣,塞console,打開developer tools。
但不知道是什麼原因developer tools也打不開,
看到這裡我想應該有人跟我想的一樣,會不會是cache或是什麼cookies害得。
最後成功的清除了cache,已清除了cookies
這裡附上一個react 清除cache的commend line

1
2
1. npm cache clean --force  //清除npm cache
2. npm start -- --reset-cache //清除react cache

以為這樣就有用了嗎…
不.. 根本沒有用 網頁還是渲染不出來.. 也一直保持著無回應的狀態…
看到這邊說不定已經有人發現問題是什麼了,
問題就是我在Footer.js裡面給的text title 跟app.js 裡面給的

用一樣的名字
這樣就會反覆的render最後就會在回圈裡面無限的輪迴了。

心得:
沒有想過自己可以學會用react 來製作一個小作品而且還用上了資料庫,
打了一對自己來說很長的文章,
一一的解析自己寫出來的東西,
慢慢地又往前了一點,這一路上很謝謝幫助我的人,也很謝謝在背後鼓勵我的人,
不會忘記這個時候的自己,持續的進步,跟最初給自己的期望一樣。

其他

1
2
3
4
5
6
7
8
9
10
11
12
13
import PropTypes from 'prop-types';

class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
//可以定義default
Greeting.propTypes = {
name: PropTypes.string
};

參閱:React-Typechecking With PropTypes
參閱:Using the State
參閱:新一代 React API — React Hooks
參閱:w3c onchange event