嗨囉!大家好!最後三天最難熬!昨天已經能夠展開我們的編輯區了,今天來把編輯資料的部分做完吧!
那一開始先來做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頁面連結
剩下的明天會把輸出的排序處理好,因為標記重要的在最上面,已完成的要在底部,最後一天的話就直接把另外兩個頁面處理完,一個輸出已完成、另一個輸出重要的事項,就剩最後兩步了!
那感謝各位大大的觀看,如果有任何問題會是需要改進的地方,還請大家留言告訴我,小弟會盡快修改或補充文章內容,謝謝大家