Hihi!大家好!這樣子的開頭還要在鐵人賽結束前繼續用下去,請大家多多包涵XD,昨天我們已經能夠從Redux
中取到資料了,今天要來說說該怎麼把透過React
來觸發寫在Reducer
中的事件吧!
開始之前要先前情提要一下,這個目錄是昨天將留言資料給列出來的最後結果,今天要從這個進度開始,把留言功能給補上去!
store
事件處理既然要對store
中的資料做處理,那在兩天前的文章中也有提到說,必須先將寫好要觸發事件的指令物件,再透過Reducer
描述哪些指令分別要處理什麼事情,所以我們先來到index.js
中把事件寫好!
昨天的程式碼中,我們有記錄著一個addMessage
的指令:
//設定動作
const addMessage = article => ({type:'addMessage',payload:article})
不過這個指令還沒有在Reducer
中描述該做哪些事情,今天就來把它寫好吧!
//Reducer
const rootReducer = (state = data, action) => {
//由action傳入的事件判斷指令為何
switch (action.type) {
//如果接收到addMessage的話
case "addMessage":
/*指定key值為現有長度+1,如果有刪除功能就不能這麼取了,
但現在沒有,所以就簡單做*/
action.payload.key = String(state.message.length+1)
//這裡把接收到的資料payload增加到message的陣列中,並回傳整個state的內容
return { ...state, message: [...state.message, action.payload] }
default:
return state
}
}
就這樣子,簡單地寫完addMessage
的事件之後回傳一個被改變後的新state
!
至少我是這麼覺得啦XD,如果對ES6
的語法不太熟的大大,應該和我一樣會對下面這一行覺得困惑:
{ ...state, message: [...state.message, action.payload] }
到底什麼是...
?他是ES6
新增的語法,叫做「展開運算子(Spread operator)」,他能做到將物件或是陣列內的內容拆開成獨立的變數。
例如上方那一行可以分成兩部分來看,第一個是合併陣列:
[...state.message, action.payload]
state.message
這是原有陣列資料,後面的action.payload
是我們要加入陣列的資料,透過...
語法就能夠將action.payload
的內容拆開和state.message
一起放到新的陣列中並回傳。
第二個部分就是物件
{ ...state, message: [...state.message, action.payload] }
他的原理也是一樣,增加一個message
屬性到原本的state
中,並回傳一個新的物件。
這時候大家可能會有一個疑問,為什麼要這麼麻煩?不能直接用.push
把action.payload
加到陣列中就好嗎?
當然可以,但是這樣子就會改變從後端取來的正確資料了!從server傳過來的資料應該讓他保持原貌會比較好!
寫好後,記得之前讓React
的message.jsx
檔案接收到store
的資料時做了什麼事情嗎?就是export default
!所以現在message.jsx
也要能夠接收到我們設定的動作,就必須也把上方設定的指令addMessage
給匯出!
但是上一篇我們已經將index.js
的匯出物件設定default
為store
了,現在要匯出一個以上的物件,我們得把default
給拿掉,所以在index.js
的最後一行把export default store
給改成:
export {store,addMessage}
那Redux
這邊的處理就先告一段落了!接下來繼續修改message.jsx
!
再進行處理前記得要先將剛剛從idnex.js
匯出的addMessage
給import
進來:
import {addMessage} from "./index.js"
另外這邊要注意,因為store
不再是index.js
檔案default
匯出的物件了,所以import store
的這一行要改寫成:
import {store} from "./index.js"
最後整理一下因為這兩行都是從.index.js
中匯入的,所以可以寫成這樣比較好看:
import {store,addMessage} from "./index.js"
//上一回同時從react-redux匯進來的Provider和connect也可以順便整理
import {Provider,connect} from "react-redux"
因為我們要寫的是留言的功能,所以簡單用React
寫一個組件來讓使用者輸入:
class InputMessage extends React.Component {
constructor(props){
super(props)
this.state = ({name:'',message:''})
this.clearMessage = this.clearMessage.bind(this)
this.changeState = this.changeState.bind(this)
this.submitMessage = this.submitMessage.bind(this)
}
changeState(event){
this.setState({[event.target.name]:event.target.value})
}
clearMessage(){
this.setState({name:'',message:''})
}
submitMessage(){
/*key值在這邊先給空的,新值會由reducer中處理給他*/
let messageData = {
key:'',
name:this.state.name,
message:this.state.message,
}
this.props.addMessage(messageData)
this.clearMessage()
}
render(){
return(
<div>
暱稱:<input type="text" name="name"
value={this.state.name}
onChange={this.changeState} />
<br/>
訊息:
<br/><textarea name="message"
value={this.state.message}
onChange={this.changeState}></textarea>
<input type="button" value="送出留言"
onClick={this.submitMessage} />
</div>
)
}
}
上面都是之前學過的東西,應該沒什麼大問題,接下來就要實作該如何將事件this.props.addMessage(messageData)
傳入這個InputMessage
組件中:
先用mapDispatchToProps
函式整理組件中需要的方法,這裡我們需要的是剛剛匯入的addMessage
,所以建立他:
const mapDispatchToProps = dispatch => {
return {
addMessage: article => dispatch(addMessage(article))
}
}
mapDispatchToProps
函式可以透過connect
的第二個參數傳遞,而因為該組件並不需要取得任何資料,所以在沒有第一個參數mapStateToProps
的情況下直接傳入null
:
const Input = connect(null,mapDispatchToProps)(InputMessage)
但是因為昨天已經有用connect
處理過MessageList
的部分:
const List = connect(mapStateToProps)(MessageList)
所以既然這兩個組件到最後都會放到MessageForm
中,那為何不直接先放進去後再做一次connect
呢?
沒錯!我們可以把程式反過來,昨天是先把MessageList
給connect
成List
後再放進MessageForm
中,但如果MessageForm
中的組件一多,用在connect
的程式碼就會越來越多,看了也不舒服,所以不如先將所有需要connect
的組件都先放到MessageForm
中,最後在一次將MessageForm
做connect
處理就好!
首先要將昨天的MessageForm
改寫,我順便在名稱前方加上Connect,表示是需要用來經過connect
處理的,處理完後產生的組件再直接命名成MessageForm
,這樣比較好辨識,不然命名完一個組件connect
後又要再取一個名字實在有點麻煩XD:
class ConnectMessageForm extends React.Component {
render(){
return(
<div>
{/*把兩個組件放進來,一個需要資料一個需要事件
這裡用props來傳,因為ConnectMessageForm等等會被connect
資料也是傳到他的props中*/}
<InputMessage addMessage={this.props.addMessage} />
<MessageList data={this.props.data} />
</div>
)
}
}
組件寫好就把他connect
吧!在這時候把MessageList
需要的資料和InputMessage
需要的事件一併傳進去處理!
//connect第一個參數是資料,第二個是事件之後把結果放到MessageForm中
const MessageForm = connect(mapStateToProps,mapDispatchToProps)(ConnectMessageForm)
最後的最後,把MessageForm
用ReactDOM.render()
輸出到畫面上吧!貼心小提醒,記得要把MessageForm
組件放在Provider
中哦!
ReactDOM.render(<Provider store={store}>
<MessageForm />
</Provider>,
document.getElementById('root'))
大功告成啦!結果如下:
一個留言版就這麼誕生了!雖然還沒有寫CSS
看起來有點兩光XD,被榨乾的現在我突然有點詞窮,哈哈哈哈,總之還是附上GitHub的連結:
GitHub程式目錄連結
GitPage連結
今天講的有點雜,不只多提到了mapDispatchToProps
傳遞事件、用了一些ES6
的語法、還改寫了昨天的程式,所以如果有任何問題都可以留言告訴我!
最後感謝各位大大的觀看,如果文章中有任何錯誤,或解釋不清楚的地方還麻煩留言告訴我,小弟我會盡快修正或補充文章內容的!謝謝大家
參考資料:
...
是展開運算子(spread operator)吧?
沒有錯!非常謝謝你勘誤
我那天可能打到有點ㄎㄧㄤ了,我立馬修正文章內容!感激不盡!
嘿嘿嘿我可是有認真看你文章呢
有你們在我就可以放心打文章了 XD