iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 27
5
Modern Web

一步一腳印的React旅程系列 第 27

[筆記][React]來做個作品吧!待辦事項「todolist」篇(7)-讓組件重用起來

嗨嗨!大家好啊!今天來輕鬆一點點(其實每天都不太難啦XD),昨天已經能將資料用迴圈寫進組件輸出到畫面上了!接下來我們來展開資料的編輯畫面!這篇會採組件重用的邏輯來做到這件事情,那進入正文吧!


首先看一下資料編輯的畫面:
https://ithelp.ithome.com.tw/upload/images/20181026/20106935Zs1dAe1XA0.png

是不是覺得有點熟悉?沒錯!他就跟新增時的畫面一模一樣!所以我們也不需要再重新做一個組件了,直接使用之前做的InputTask就好,那要改的時候先打開昨天製作的List.jsx檔案,我們需要在裡面加上的東西有幾點:

  1. List上設定點擊事件,點擊後可以將InputTask渲染出來。
  2. List接收到的待辦事項資料經由props傳到InputTask中。
  3. 然後就...沒了!!!!

為了第三點的到來,就趕緊開工吧XD,一起打開List.jsx

  1. 先在state中多新增一個屬性,幫我們保管要輸出的InputTask組件內容:
    constructor(props) {
        super(props)
        this.changeState = this.changeState.bind(this)
        this.list = React.createRef()
    
        this.state = {important:this.props.listData.important
                     ,complete:this.props.listData.complete
                     ,editTasks:null}
    }
    
    這樣子我們在render的時候就能夠固定輸出他,什麼意思呢?看下去吧!
  2. render中輸出剛剛的this.state.editTasks內的值,因為他平常是null,所以一直待著也不會有什麼影響:
    render() {
        //初始化組件
        return (
            <div class="listBlock">
                <div class={' list ' + 
                    (this.state.important == 'Y' ? ' important ' : '')}>
                    {/*因為怕太長所以中間省略*/}
                </div>
    
                {/*在這裡固定輸出this.state.editTasks*/}
                <div>
                    {this.state.editTasks}
                </div>
            </div>
        )
    }
    
  3. 再來設定點擊事件,點擊後要把InputTask組件寫到this.state.editTasks中,所以新增以下事件設定this.state.editTasks的值,並同時把待辦事項的資料給InputTask
    openEdit() {
        this.setState({editTasks:(<InputTask listData={this.props.listData} />)})
    }
    
    既然有打開那也有關閉,等等要把關閉用props傳給InputTaskcloseAdd中,記得我們很久很久之前有寫過這個吧XD
    closeEdit() {
        this.setState({editTasks:null})
    }
    
    搞定後記得把他傳進InputTask中,所以把上方的openEdit改成下面這樣:
    openEdit(event) {
        this.setState({editTasks:(<InputTask closeAdd={this.closeEdit}
                                            listData={this.props.listData} />)})
    }
    
    記得把openEdit設定給List組件的點擊事件:
    render() {
        //初始化組件
        return (
            <div class="listBlock">
                <div class={' list ' + 
                    (this.state.important == 'Y' ? ' important ' : '')}
                    onClick={this.openEdit}>
                    {/*因為怕太長所以中間省略*/}
                </div>
            </div>
        )
    }
    
  4. 阿我們鏡頭一轉到許久不見的InputTask.jsx做一些小調整,其實沒有太複雜,就只是因為當初在這裡預設state的值全都是空的,但現在不同了,有可能我會接收到從外面傳進來的值,所以在這種情況下我們得在設定state時做一些小調整:
    constructor(props) {
        super(props)
        //如果有值的話就寫入值,沒值就預設都空的
        if (this.props.listData) {
            this.state = this.props.listData
        }
        else {
            this.state = { id: '', name: '', date: '', time: '', file: ''
                        , commit: '', important: '', complete: false }
        }
     }
    

就是上方簡簡單單的調整,那執行來看看吧!

登愣!野生的Bug出現了!當我點下List的任何一處後,雖然編輯畫面跳出來了,但是List本身卻沒有消失:
https://ithelp.ithome.com.tw/upload/images/20181026/201069350R7gfXLMLC.png

而且當我只是單純要將它變成已完成或標記重要時,他依然會開啟編輯畫面,總之這兩個問題我們一個一個解決!

  1. 為了讓List可以在點擊後隱藏,就必須要可以先抓到那個div,但是在組件可以重複使用的狀況下,通常不會為組件內的DOM設定id,否則當組件重用時,就只會有第一個設定id的生效而已,那該怎麼辦呢?就用ref吧!
    1. ListconstructorcreateRef()
      this.list = React.createRef()
      
    2. 指定ref給點擊後需隱藏的div
      render() {
        //初始化組件
        return (
            <div class="listBlock">
                <div class={' list ' + 
                      (this.state.important == 'Y' ? ' important ' : '')}
                      onClick={this.openEdit}
                      ref={this.list}>
                      {/*中間省略*/}
                  </div>
                  {/*下面雖然不長但也省略*/}
              </div>
          )
      }
      
    3. 然後改寫一下openEditcloseEdit兩個事件,當點擊打開時就為他的CSS增加display=none做隱藏,關閉時再把displaynone拿掉讓他顯示:
      openEdit() {
          this.list.current.style.display = 'none'
      
          this.setState({editTasks:(<InputTask closeAdd={this.closeEdit}
                                          listData={this.props.listData} />)})
      }
      
      closeEdit() {
          this.list.current.style.display = ''
          this.setState({editTasks:null})
      }
      
  2. 當標記完成或重要時不執行openEdit,這個其實很簡單,只需要去判斷觸發openEdit事件的元件是什麼就好,那問題來了,該怎麼知道呢?就決定是你了!event!從event判斷如果class不含fa-startaskChk才顯示編輯畫面:
    openEdit(event) {
        if (event.target.className.indexOf('fa-star') === -1 &&
            event.target.className.indexOf('taskChk') === -1) {
            this.list.current.style.display = 'none'
    
            this.setState({editTasks:(<InputTask closeAdd={this.closeEdit}
                listData={this.props.listData} />)})
    
        }
    }
    

處理完上面兩個Bug後就能重新執行網頁了!這時候大家的頁面應該不只可以新增,還能夠透過點擊待辦事項的列表顯示詳細資料!只是目前+ SAVE這個按鈕的事件還是新增,明天我們再來將修改資料的事件寫好!讓他能夠再InputTask中修改待辦事項的資料!那以下附上今天進度的GitHub連結:
GitHub程式目錄連結
GitPage頁面連結


到這裡其實會發現,在實作的時候都一一用上前20篇學的所有精華(算精華嗎XD),最後這個作品就算是一個總複習,讓大家也讓我更熟悉組件的開發模式!那最後還是感謝各位大大的觀看,如果文章中有任何問題或是錯誤的地方,還麻煩留言告所我,小弟都會盡快查看並修改或補充文章內容的!謝謝大家/images/emoticon/emoticon41.gif


上一篇
[筆記][React]來做個作品吧!待辦事項「todolist」篇(6)-上吧!迴圈!
下一篇
[筆記][React]來做個作品吧!待辦事項「todolist」篇(8)-人生不能重來,但資料可以修改
系列文
一步一腳印的React旅程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言