iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 12
4
Modern Web

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

[筆記][React]使用React開發的思考模式(3)-最後來個小範例吧!

這一節我們來用前幾篇文章學到的一切來做個小範例吧!不知道大家在學網頁時,第一個嘗試的是什麼?當然這邊指的是除了Hello world!之外的XD,小弟我啊!遙想起三年前某個晚上下班時,看到朋友做的留言版,就莫名走上IT這條路了,所以今天也來用react做個留言板吧!

不過雖說是要做個留言板,但不會整個都做啦!簡單把渲染出留言的部分做出來,然後可以使用搜尋匡查詢出留言者就好!那讓我們看看怎麼做吧!


確定目標

哈哈!這個階段其實是我自己想的,不過這卻是在動手之前相當重要的事情,畢竟要好好思考怎麼做,才不會做白工,所以假設我們拿到的畫面像這樣子:
https://ithelp.ithome.com.tw/upload/images/20181004/20106935TWLKsyWqh2.jpg
嗚嗚,不要笑,這只是個簡單小範例對吧!

當我們拿到畫面時,就要把他切成一塊一塊的組件,然後用組合的方式把它合起來,而一個組件盡量只有一個功能,如果有兩個,那就把他切成兩個組件處理!這個叫做單一功能原則,所以根據原則判斷,可以把上方的留言畫面切成幾個組件處理:
https://ithelp.ithome.com.tw/upload/images/20181004/20106935ELrZ8Ywl70.jpg

讓我們從最小的組件看到最大:

  1. 橘色框框的是每一筆留言,記錄著留言者及留言訊息。
  2. 綠色框框把所有的留言都包住,包含所有留言的區塊。
  3. 紅色框框裡面是搜尋條件,下方的留言會根據條件做篩選。
  4. 藍色框框則是包含了留言區塊和條件區塊,也是我們最後輸出的組件。

根據上方的結論,我們就可以先寫下這四個組件的class

class Message extends React.Component{

}

class MessageBlock extends React.Component{

}

class SearchBlock extends React.Component{

}

class MessageForm extends React.Component{

}

//最後只輸出MessageForm
ReactDOM.render(<MessageForm />, document.getElementById('root'))

先輸出留言

在開始的第一步,我們應該都會先取得後端傳過來的資料,假設他長這樣子:

//訊息資料
let data = [{id:'1',name:'神Q',message:'嗨!大家好啊!'},
            {id:'2',name:'小馬',message:'早安啊!昨天有沒有好好發文?'},
            {id:'3',name:'王子',message:'ㄛ!別說了,那真的超級累!'},
            {id:'4',name:'神Q',message:'哈哈哈!加油啦!再一下就結束了!'},
            {id:'5',name:'王子',message:'結束後我一定要爆睡一頓!'},]

那我們應該會透過props將這個data資料傳進MessageForm中,因為我們最後也只輸出這個組件,所以ReactDOM.render會變成:

//把訊息資料傳進MessageForm中
ReactDOM.render(<MessageForm messageData={data} />
                            , document.getElementById('root'))

傳進去後就可以先處理輸出訊息的部分,搜尋放最後再做,畢竟沒有看到留言也沒辦法測試搜尋正不正確:

class MessageForm extends React.Component{
    render(){
        return(
            <div>
                {/*透過props的單向資料流,再將訊息傳入MessageBlock中處理*/}
                <MessageBlock messageData={this.props.messageData} />
            </div>
        )
    }
}

既然資料已經傳到MessageBlock中了,接著就要把每筆的留言資料用Message組件輸出了,因為只是把陣列裡面的資料一筆一筆放到Message,所以可以用迴圈處理:

class Message extends React.Component{
    render(){
        //這邊做一些簡單的樣式,不然留言會全部擠在一起
        let divStyle={marginBottom:20}
        let messageStyle={marginLeft:20}
        return(
            <div style={divStyle}>
                {/*把每筆傳進來的留言資料都放好後回傳*/}
                <div>{this.props.name}對大家說:</div>
                <div style={messageStyle}>{this.props.message}</div>
            </div>
        )
    }
}

class MessageBlock extends React.Component{
    render(){
        //用map迴圈把每筆留言的資料都用props傳進Message組件放好,再指定給message
        let message = this.props.messageData.map((item)=>{
                return <Message key={item.id} name={item.name} message={item.message} />
        })
        return (
            <div>
                {/*回傳放完留言資料的message變數*/}
                {message}
            </div>
        )
    }
}

從上方的例子中可以看到,從最外層的MessageForm組件到最裡面的Message都是利用props來傳輸資料,組件內的資料是從外面單向流到裡面,這樣可以確保資料是不會在不同組件中被異動且正確的!

經過上方的努力,畫面上已經可以看見留言資料了:
https://ithelp.ithome.com.tw/upload/images/20181004/20106935pIJZyWHi1Q.png

查詢留言資料

既然我們已經有資料了,就可以來處理查詢這一塊,查詢的組件畫面很簡單,只需要一段文字和一個輸入框:

class SearchBlock extends React.Component{
    render(){
        return(
            <div>
                <span>搜尋留言人:</span>
                <input type="text" />
            </div>
        )
    }
}

畫面處理好後,把SearchBlock加進MessageForm中,並在訊息間插個分隔線的標籤<hr>

class MessageForm extends React.Component{
    render(){
        return(
            <div>
                <SearchBlock />
                <hr/>
                {/*透過props的單向資料流,再將訊息傳入MessageBlock中處理*/}
                <MessageBlock messageData={this.props.messageData} />
            </div>
        )
    }
}

目前為止已經將畫面都做出來了:
https://ithelp.ithome.com.tw/upload/images/20181004/20106935AmoshXm1GQ.png

但這並不是我們的目標對吧!現在那個輸入框就像裝飾用的一樣,根本沒有任何功能,再下一步之前先試想一下,當我們在SearchBlock內的輸入框輸入資料時,要根據我們輸入的值來篩選MessageBlock中的訊息內容,所以這兩個組件間同時間需要一個相同值,有沒有很熟悉?想想兩天前我們做了什麼...

沒錯!就是將共同的值放到最近的共同父組件的state中!而他們的共同父組件就是MessageForm!改寫時間又到了,增加constructor吧:

class MessageForm extends React.Component{
    constructor(props){
        super(props)
        //增加了state.name用來放篩選留言者的值
        this.state = ({name:''})
        //照慣例也新增個changeState用來在使用者輸入值的時候觸發事件,改變state
        this.changeState = this.changeState.bind(this)
    }
    
    //更新使用者目前輸入的值到state中
    changeState(event){
        this.setState({name:event.target.value})
    }

    render(){
        return(
            <div>
                {/*把state中的name和執行的事件都用props給SearchBlock*/}
                <SearchBlock searchName={this.state.name}
                                changeState={this.changeState} />
                <hr />
                {/*這裡也要傳入state的name用來篩選留言*/}
                <MessageBlock messageData={this.props.messageData} 
                                searchName={this.state.name} />
            </div>
        )
    }

先回到剛剛的SearchBlock中,把傳進來的資料和事件安置好:

class SearchBlock extends React.Component{
    render(){
        return(
            <div>
                <span>搜尋留言人:</span>
                {/*這裡將MessageForm的state.name給輸入框的value,
                並設定onChange再值改變的時候可以執行changeState事件*/}
                <input type="text" 
                        value={this.props.searchName}
                        onChange={this.props.changeState} />
            </div>
        )
    }
}

最後要處理的地方是輸出訊息的區塊,記得我們是在哪一個組件中使用Message的嗎?就是在MessageBlock的迴圈內,這裡的寫法很單純,只需要判斷每個訊息的留言人裡面是否含有props.searchName的值,如果有就跑Message,沒有就不做處理:

class MessageBlock extends React.Component{
    render(){
        let message = this.props.messageData.map((item)=>{
            //在這裡用if判斷留言者item.name中是否含有this.props.searchName的值,如果有就執行,沒有就不動作
            if(item.name.indexOf(this.props.searchName)!=-1)
                return <Message key={item.id} name={item.name} message={item.message} />
        })
        return (
            <div>
                {message}
            </div>
        )
    }
}

來到這裡的各位可以給自己一點鼓勵!因為我們完成了!結果如下:
https://ithelp.ithome.com.tw/upload/images/20181004/20106935T2CutXPdbS.png

他只會依照輸入框中的值去匯出符合的留言資料!以下附上GitHub和GitPage:
GitHub連結
GitPage連結

實際做起來不難吧!只是把前幾篇的所有文章都做個大整理XD,從classpropsstateiffor、事件觸發,全部都做到了XD,有沒有覺得不知不覺也學到不少了,哈哈哈!可以帶著這份成就感明天跟著我繼續學習!


最後感謝各位大大的觀看,如果文章中有任何錯誤會解釋不清楚的地方,還麻煩留言告訴我,小弟會盡快修正文章內容和改進的,謝謝大家/images/emoticon/emoticon41.gif

參考文章:

  1. https://reactjs.org/docs/thinking-in-react.html

上一篇
[筆記][React]使用React開發的思考模式(2)-組件的構成模式
下一篇
[筆記][React]當React遇上Redux(1)-初次見面
系列文
一步一腳印的React旅程30

1 則留言

1
Homura
iT邦高手 3 級 ‧ 2018-10-18 09:12:24

這些對話是不是你的心聲啊?/images/emoticon/emoticon04.gif

哈哈哈哈,
老實說是的XD
每天都在惦記著文章進度/images/emoticon/emoticon37.gif

我要留言

立即登入留言