嗨囉!大家好啊!就像上一期(還是上上一期?)最後說的,今天就要來處理新增資料啦!話說剛剛在準備進度時,都沒經過任何測試,一次執行就過,讓我非常驚訝,一直在試是不是有哪裡忘了做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,不過這樣子用文章記錄著作品慢慢完整的感覺還滿有趣的!至少是從來沒有過的體驗,如果有任何建議還麻煩各位大大留言告訴我,小弟我會非常感激的,哈哈哈。
最後也感謝各位大大的觀看,如果有文章中有任何錯誤或是講解不清楚的地方,還麻煩大家留言告訴我,小弟會盡快修正或補充文章內容的!謝謝大家![]()