嗨囉!大家好啊!就像上一期(還是上上一期?)最後說的,今天就要來處理新增資料啦!話說剛剛在準備進度時,都沒經過任何測試,一次執行就過,讓我非常驚訝,一直在試是不是有哪裡忘了做XD,那接下來就來度過這順利的一天吧!
State
首先要在新增待辦事項的表單組件中設定state
,把待辦事項的資料都寫到State
中,最後再將State
送給Redux
處理就好!而新增的表單組件是InputTasksForm
,但是待辦事項的名稱是在InputTask
中,秉持著把State
提升到共同組件的精神,應該要選擇在InputTask
中設定State
資料,不過先別說得太複雜,我們一樣一步一步把它完成:
InputTask.jsx
constructor
設定state
,欄位就像我們昨天提出的那樣,另外考慮到有一個檔案上傳的input
沒辦法設定value
,所以出動ref
處理:
constructor(props){
super(props)
this.state = {id:'',name:'',date:'',time:'',file:'',commit:'',important:'',complete:false}
this.filebox = React.createRef()
}
state
,下方的做法是去判斷發生改變的是哪個欄位,就取他的name
屬性,然後寫進對應的state
物件的key
中,所以我們等等記得要設定name
值:
changeState(event){
//取目前發生改變的值
let value = event.target.value
//如果是檔案的話因為會有路徑,所以這裡只抓檔名
if (event.target.name === "file"){
//去取最後一個出現的「/反斜線」到最後一個字就是檔名了
value = value.substring(value.lastIndexOf('\\')+1)
}
//checkbox也另外處理,因為沒有值
else if (event.target.name === "complete"){
value = event.target.checked
}
//設定值給對應的name欄位,所以我們組件的name都要設的和state中的名稱一樣
this.setState({[event.target.name]:value})
}
變動state
的番外篇!其實也不算番外啦!只是todolist
的有些資料不是透過input
填寫的對吧!像是要不要標記重要或是否完成,這兩個資料,他們分別會透過點擊checkbox
和星星圖案的Icon
觸發,我們就在這裡寫下他吧!
先寫下complete
的樣式內容,到index.css
中增加!
/*加上刪除線*/
.complete{
text-decoration: line-through;
color: #9B9B9B;
}
標記已完成,要在checkbox
上綁定state
中的資料,還有增加onChange
事件,讓這邊的異動可以連動更新state
:
<input name="complete" type="checkbox" class="taskChk"
checked={this.state.complete}
onChange={this.changeState} />
最後在待辦事項的input
中的class
寫判斷式,讓他可以根據state
中的值改變樣式:
<input name="name" type="text" placeholder="Type Something Here…"
class={'taskTitle' +
(this.state.complete ? ' complete ' : '')}
value={this.state.name}
onChange={this.changeState} />
掰惹位,既然上方都改到他了,就順便綁定state
的資料和設定onchange
事件。
點擊星星標記重要,這個要再星星的Icon
上增加onClick
事件,這邊因為也要更改他的選取後的樣式,所以一樣先寫下class
:
/*更換背景顏色*/
.important{
background: #FFF2DC;
}
/*更換icon顏色*/
.iconImportant{
color: #F5A623;
}
為星星的Icon
設定點擊事件,標記重要後,不只星星本身,連div
和待辦事件名稱的input
都要加入判斷式,確認是否要增加上方的class
:
<div class={this.state.important == 'Y' ?
'important inputTaskTitle' : 'inputTaskTitle'}>
{/*其餘省略*/}
<input name="name" type="text" placeholder="Type Something Here…"
class={ ' taskTitle ' +
(this.state.important == 'Y' ? ' important ' : '') +
(this.state.complete ? 'complete' : '')}
value={this.state.name}
onChange={this.changeState} />
<i class={this.state.important == 'Y' ?
'fas fa-star fa-lg icon iconImportant' : 'far fa-star fa-lg icon'}
onClick={this.tagImportant} ></i>
{/*其餘省略*/}
</div>
事件內容如下,因為星星本身沒有value
可以控制,所以特別寫一個用來異動state
的function
:
tagImportant() {
//如果現在不是重要的就把它變重要的
if (this.state.important == '') {
this.setState({ important: 'Y' })
}
else {
this.setState({ important: '' })
}
}
Redux
新增的事件,首先要匯入要使用的事件function
:
import { addTodoList } from "../../actions"
然後在class
中使用,新增一個送出新增的事件,並在裡面把整個State
丟給剛剛匯入的事件新增:
submitTodo(){
//先檢查資料,至少要有名稱
if(this.state.name === ''){
alert('待辦事項名稱未輸入!')
}
else{
//因為state就存著資料,所以直接把state送給他新增
this.props.addTodoList(this.state)
alert('成功新增!')
//關閉新增畫面
this.props.closeAdd()
//初始化資料資料
this.setState({id:'',name:'',date:'',time:'',file:'',commit:''
,important:'',complete:false})
//不受控組件另外處理
this.filebox.current.value = ''
}
}
然後貼心小提醒是上面幾個事件都有用到this
,所以記得要在constructor
中用bind
指定this
:
constructor(props){
super(props)
this.state = {id:'',name:'',date:'',time:'',file:'',commit:'',important:'',complete:false}
this.changeState = this.changeState.bind(this)
this.submitTodo = this.submitTodo.bind(this)
this.tagImportant = this.tagImportant.bind(this)
this.filebox = React.createRef()
}
state
和changeState
要將資料寫回state
外,記得+ Save
也在InputTasksForm
中,也不要忘了剛剛為filebox
建立的ref
要指定給他,所以要一起在render
中透過props
傳進去!
render() {
return (
<div>
<div class={this.state.important == 'Y' ?
'important inputTaskTitle' : 'inputTaskTitle'}>
<input name="complete" type="checkbox" class="taskChk"
checked={this.state.complete}
onChange={this.changeState} />
{/*替該name設定對應的state名稱,
然後指定value為state中的值,
和增加onChange事件,
讓值改變時可以同時寫回`state`*/}
<input name="name" type="text" placeholder="Type Something Here…"
class={(this.state.important == 'Y' ?
'important taskTitle ' : 'taskTitle ') +
(this.state.complete ? 'complete' : '')}
value={this.state.name}
onChange={this.changeState} />
<i class={this.state.important == 'Y' ?
'fas fa-star fa-lg icon iconImportant' :
'far fa-star fa-lg icon'}
onClick={this.tagImportant} ></i>
<i class="fas fa-pen fa-lg icon icon_edit"></i>
</div>
<InputTasksForm closeAdd={this.props.closeAdd}
stateData={this.state}
changeState={this.changeState}
submitTodo={this.submitTodo}
filebox={this.filebox} />
</div>)
}
Redux
來幫忙處理資料,因此要替InputTask
組件和addTodoList
做connect
!而connect
後又會是一個新組件,所以我先改變一下InputTask
的名稱,在他前面加個Connect
:
//幫InputTask改名字
class ConnectInputTask extends React.Component {
//內容就像上方改的,文章的最後也會有今天的GitHub進度
}
connect
需要哪些參數嗎?有分資料的和事件的!現在在表單中我們只需要事件而已,所以先寫好需求,等等把需求和ConnectInputTask
一起透過connect
給Redux
,這樣他才知道我們需要什麼:
const mapDispatchToProps = dispatch => {
return {
//使用dispatch呼叫事件addTodoList操作store
addTodoList: todoList => dispatch(addTodoList(todoList))
}
}
connect
,把connect
後的組件再指定回InputTask
,這樣就不用在改其他地方了:
const InputTask = connect(null,mapDispatchToProps)(ConnectInputTask)
現在把InputTask
處理好後,還有他裡面的表單InputTasksForm
組件要處理,這個部分只需要做四件事情:
state
的name
。value
都設定為state
的值,雖然在這邊還不需給值,不過還是得讓他受控才能改變state
,阿不受控的filebox
要指定ref
給他,讓我們可以在外面控制,至於值就寫進之前為他留的span
中。filebox
都增加onChange
事件,讓他值改變時可以寫到state
中。submitTodo
指定給+ Save
按鈕的onClick
觸發。做完以上四件事情後,InputTasksForm
應該會長這樣:
import React from "react"
import { InputName } from "../InputName"
class InputTasksForm extends React.Component {
render() {
return (
<div class="InputTasksForm">
<div class="InputTask">
<InputName className="fas fa-calendar-alt" inputName="Deadline" />
<div class="inputForm">
<input name="date" type="date" class="inputStyle inputDateTime"
value={this.props.stateData.date}
onChange = {this.props.changeState} />
<input name="time" type="time" class="inputStyle inputDateTime"
value={this.props.stateData.time}
onChange = {this.props.changeState} />
</div>
<InputName className="fas fa-file" inputName="File" />
<div class="inputForm">
<input name="file" type="file" class="inputStyle"
ref = {this.props.filebox}
onChange = {this.props.changeState} /><br/>
<span class="inputStyle">{this.props.stateData.file}</span>
</div>
<InputName className="far fa-comment-dots" inputName="Comment" />
<div class="inputForm">
<textarea name="commit" rows="7" cols="55" class="inputStyle"
value = {this.props.stateData.commit}
onChange = {this.props.changeState} >
</textarea>
</div>
</div>
<div>
<button type="button" class="addButton cancelButton" onClick={this.props.closeAdd}> X Cancel</button>
<button type="button" class="addButton saveButton" onClick={this.props.submitTodo}> + Save</button>
</div>
</div>
)
}
}
export { InputTasksForm }
看起來有點長,不過別嚇到,都是我們之前做過的!
好的,那裡面和外面都做完了,接下來還有外外面XD,哈哈哈!我可沒有開玩笑,記得那些被connect
後的組件應該要放在哪裡嗎?
就是<Provider>
中!應該沒有忘記他吧?他還有個可愛的store
屬性是要指定我們create
的store
啊!好吧!沒關係,只要看到了一定會想起來的!讓我們打開負責最後輸出的Main
:
todoListStore
和Procider
,store
昨天如果匯過就不要再重複匯,不然會出錯哦!Provider
組件並指定store
屬性為todoListStore
,因為其他組件也會需要用到connect
,所以選擇在Main
中讓他包住整個頁面的組件。import React from "react"
import { Provider } from "react-redux"
import { HashRouter, Route } from "react-router-dom"
import { TopBlock } from "../TopBlock"
import { MyTasks } from "../MyTasks"
import { todoListStore } from "../../store"
class Main extends React.Component {
render() {
return (
<Provider store={todoListStore}>
<HashRouter>
<div>
<TopBlock />
<Route exact path="/" component={MyTasks} />
</div>
</HashRouter>
</Provider>
)
}
}
window.store = todoListStore
export { Main }
上方留著window.store = todoListStore
,讓我們確認資料用,因為現在還沒有把顯示資料的列表做出來,那進行到這裡可以對自己說聲辛苦了,打開網頁新增事項看看吧XD
當沒填寫名稱時:
將所有資料輸入後新增:
確定後會關閉新增畫面,且再打開時欄位都清空了:
然後用剛剛留下來的store
驗證資料有沒有新增進去:
成功啦!雖然感覺這一次調整了很多地方,但是只要跟著框架的規則,一步一步錯基本上就不會出錯了!那下方附上今天的進度GitHub:
GitHub程式目錄連結
GitPage頁面連結
明天終於可以來把資料輸出出來了!等看得到資料後,就會覺得作品像個樣子了XD,不過這樣子用文章記錄著作品慢慢完整的感覺還滿有趣的!至少是從來沒有過的體驗,如果有任何建議還麻煩各位大大留言告訴我,小弟我會非常感激的,哈哈哈。
最後也感謝各位大大的觀看,如果有文章中有任何錯誤或是講解不清楚的地方,還麻煩大家留言告訴我,小弟會盡快修正或補充文章內容的!謝謝大家