嗨囉!大家好!最後三天最難熬!昨天已經能夠展開我們的編輯區了,今天來把編輯資料的部分做完吧!
那一開始先來做Redux
的部分吧!要處理的事情有以下幾點,讓我們邊說邊做:
constants/todoAction-type.js
中新增一個指令並匯出:
export const EDIT_TODOLIST = "EDIT_TODOLIST"
actions/todoList.js
用剛剛新增的指令建構事件:
import {EDIT_TODOLIST} from "../constants/todoAction-type.js"
export const editTodoList = todoList => ({
type : EDIT_TODOLIST,
payload : todoList
})
reducers/todoList.js
中寫下該事件要怎麼處理資料(以下就只描述EDIT_TODOLIST
的部分,之前寫過的就不另外寫了):
//先匯入指令
import { EDIT_TODOLIST } from "../constants/todoAction-type.js"
const todoListReducer = (state = todoData, action) => {
switch (action.type) {
case EDIT_TODOLIST: {
//先以目前的資料去複製另一個全新的陣列
let newState = state.slice(0)
//下迴圈比對id值
for (let i = 0; i <= newState.length - 1; i++) {
if (newState[i].id === action.payload.id){
//將新的資料用splice()取代原本的位置中的資料
newState.splice(i, 1, action.payload)
break;
}
}
//回傳處理後的新資料
return newState
}
}
}
處理完Redux
後就是React
的問題了,記得我們昨天在List.jsx
中有寫下當在外面點擊完成或是標記重要時會觸發changeState
事件,來更改組件中的state
嗎?但昨天那樣子做也只是做做樣子,更新state
而已,其實在store
的資料根本沒變,所以接著把剛剛寫的editTodoList
放到裡面去。
先將editTodoList
事件匯入List.jsx
中:
import { editTodoList } from "../../actions"
需要用到store
的事件內容,所以原本的List
組件要經過connect
,以下把它改名成ConnectList
,經過connect
後再指定給List
:
class ConnectList extends React.Component {
{/*中間省略*/}
}
把connect
需要的參數mapDispatchToProps
建構出來:
const mapDispatchToProps = dispatch => {
return {
editTodoList: todoList => dispatch(editTodoList(todoList))
}
}
接著把List
組件和mapDispatchToProps
做connect
:
//記得匯入connect
import { connect } from "react-redux"
//因為只用到事件沒用到資料,所以第一個參數給null
const List = connect(null, mapDispatchToProps)(ConnectList)
List
組件在經過connect
後就能夠透過this.props.editTodoList
來執行更新,那我們要在標記完成或重要時去執行,也就是在changeState
裡面多執行一段送到redux
更新的程式,以下先在ConnectList
中另外寫一段function
:
class ConnectList extends React.Component {
constructor(props) {
//其餘省略
//在constructor中指定執行時的this
this.updateTodolist = this.updateTodolist.bind(this)
//其餘省略
}
//其餘省略
updateTodolist(){
//複製一份新的物件,為該代辦事項的資料
let updateList = Object.assign({},this.props.listData)
//用之前學過的解構賦值把complete和important兩個欄位替換成state的值
updateList = {...updateList
,complete:this.state.complete
,important:this.state.important}
//透過editTodoList丟到redux更新
this.props.editTodoList(updateList)
}
//其餘省略
}
最後把該updateTodolist
放到changeState
中setState
的第二個參數,記得不要直接放在下一行程式執行,因為會有執行緒的問題:
changeState(type) {
//以下setState的第二個參數都是更新的函式
switch (type) {
case "complete": {
this.setState({ complete: window.event.target.checked }
,this.updateTodolist)
break;
}
case "important": {
if (this.state.important === '')
this.setState({ important: 'Y' },this.updateTodolist)
else
this.setState({ important: '' },this.updateTodolist)
break;
}
}
}
經過上方的調整,就可以先來看看外層List
在標記已完成或是否重要後的資料變化:
修改前:
修改後:
外面的List
處理完後要來處理裡面表單的部分了,這邊比較特別的是,因為在編輯時也可以點選已完成或標記重要,但使用者在做這個動作的時候應該不需要點選+ SAVE
就能改變狀態,所以這裡我的處理方式先將在List
組件中的changeState
事件傳入InputTask
中,所以在openEdit
中的setState
改成下方這樣子(這裡也一併將修改資料的editTodoList
也傳進去,之後再說明這部分):
this.setState({editTasks:(<InputTask closeAdd={this.closeEdit}
listData={this.props.listData}
changeState={this.changeState.bind(this)}
editTodoList={this.props.editTodoList} />)})
將List
組件的changeState
事件是針對標記重要和完成用的,所以在InputTask
中更改這兩個欄位的tagImportant
和changeState
都要在另外觸發this.props.changeState
事件,將資料同步更新到外面List
組件的state
,同時又會觸發生命週期的componentDidUpdate()
去更新store
的資料。
而上面的做法也要考慮到在新增時是不會有this.props.changeState
傳入的,所以在constructor
中先去設定該事件,如果是新增就不要執行,不然會出錯:
class ConnectInputTask extends React.Component {
constructor(props) {
//其餘省略
//如果有該事件就執行,沒有代表是新增狀態,並重新命名為changeListState
this.changeListState = type =>{
if(this.props.changeState)
this.props.changeState(type)
else
console.log('新增狀態所以沒有this.props.changeState')
}
//其餘省略
}
//以下省略
}
指定完事件就能在完成和重要這兩個資料改變時觸發他了:
tagImportant() {
if (this.state.important === '') {
this.setState({ important: 'Y' })
}
else {
this.setState({ important: '' })
}
//一併更新狀態到外面的`List`組件去
this.changeListState('important')
}
changeState(event) {
let value = event.target.value
if (event.target.name === 'file') {
value = value.substring(value.lastIndexOf('\\') + 1)
}
else if (event.target.name === 'complete') {
value = event.target.checked
//一併更新狀態到外面的`List`組件去
this.changeListState('complete')
}
this.setState({ [event.target.name]: value })
}
來看看結果吧!當資料在編輯表單時點選已完成和標記重要:
修改前:
修改後:
也來看看新增時標記:
都好後剩下的部分最簡單,InputTasksForm
組件中的+ SAVE
是由InputTask
組件將submitTodo
傳入做新增的,現在只要調整submitTodo
,讓他可以再新增時執行新增,編輯時執行剛剛從List
用props
傳進來的editTodoList
事件就好了!
這裡我用id
簡單判斷,因為如果是新增就沒id
,編輯的話資料本身就會有id
,所以submitTodo
改成這樣子判斷:
submitTodo() {
if (this.state.name === '') {
alert('待辦事項名稱未輸入!')
}
else {
//判斷id是否有值
if (this.state.id === '') {
this.props.addTodoList(this.state)
alert('成功新增!')
}
else {
//有的話就執行編輯
this.props.editTodoList(this.state)
alert('編輯成功!')
}
this.setState({ id: '', name: '', date: '', time: '', file: '', commit: ''
, important: '', complete: false })
this.filebox.current.value = ''
this.props.closeAdd()
}
}
上方我是編輯後就將表單關掉,如果表單要停留在編輯畫面的話就把最後那三行放到alert('成功新增!')
下面。
搞定後來看看結果吧!
修改前:
修改後:
資料就變了,點開也是修改後的資料:
有沒有覺得大功告成的感覺XD,其實因為是第一次實作組件開發,所以程式中應該還有不少地方可以做優化,如果各位大大有任何建議或是小弟我使用方法錯誤,還請留言告訴我,那以下附上今天的GitHub進度:
GitHub程式目錄連結
GitPage頁面連結
剩下的明天會把輸出的排序處理好,因為標記重要的在最上面,已完成的要在底部,最後一天的話就直接把另外兩個頁面處理完,一個輸出已完成、另一個輸出重要的事項,就剩最後兩步了!
那感謝各位大大的觀看,如果有任何問題會是需要改進的地方,還請大家留言告訴我,小弟會盡快修改或補充文章內容,謝謝大家