今天
本來要直接就昨天的問題解決
就在那個準備要連接這些組件的時候
發現一個天大的問題
那就是...
我還有個登入的東東阿!(( 腦中無限東東echo ))
所以一個轉舵
從登入開始寫起
由於不牽扯後台
這個好尊重個資的登入系統必須要做到三件事情
分別是 記錄使用者名稱、 登入到首頁 和 從首頁登出
三件事情分別定義 Actions
// actions
export function setUser( user ){
return {
type : SET_USER,
user : user
}
}
export function userLogin(){
return {
type : LOGIN
}
}
export function userLogout(){
return {
type : LOGOUT
}
}
在寫成這樣之前
天真的我想說可以讓使用者按下按鈕的時候
在同步檢查是不是真的有名字並切換狀態
後來發現
這樣可以
但就必須破壞成就拿掉表單
成就當然要好好保留阿!
所以就以原本改變狀態的寫法
直接轉換成資料流寫法
所以就成為現在的模樣啦
另外
我把 Reducer 分開成兩個
由於登入與否和遊戲的進行互不影響
所以我就把各自的資料分開
處理器也分開
根據目的選擇使用的資料對象
避免資料汙染的狀況出現
// reducer.js
const loginData = {
user : "",
isLogin : false
}
const oxData = {
playerMark : "O",
OXgame:[
["1","2","3"],["4","5","6"],["7","8","9"]
],
isWinner:""
}
function isLogined(state = loginData, action){
switch(action.type){
case SET_USER:
// console.log(action.user.target.value);
return Object.assign({}, state, {
user : action.user.target.value
});
case LOGIN:
if(state.user == "")
window.alert("阿阿阿您忘記名字了阿阿阿!");
return state;
else
return Object.assign({}, state, {
isLogin : true
});
case LOGOUT:
return Object.assign({}, state, {
user : "",
isLogin : false
});
default:
return state;
}
}
function OXGame(state = oxData, action){
...
}
透過combineReducers
的函式
可以讓這些 Reducer 結合成一個大Reducer
但在這個大 Reducer 內部卻是各自為政這樣
設定好這兩者之後
建立一個loginContainer
作為 Container 組件
透過他來選擇會使用到的資料以及行為
const mapStateToProps = (state) => {
return {
user: state.isLogined.user,
isLogin : state.isLogined.isLogin
}
}
const mapDispatchToProps = (dispatch) =>{
return {
setUser: (user) => {
dispatch( setUser(user) )
},
login: () => {
dispatch( userLogin() )
},
logout: () => {
dispatch( userLogout() );
}
}
}
藉由connect
函式把它和呈現資料的組件連接
也就是我們最主要的介面 App
也因此main.js
的根組件就得要調整成這個 Container 組件啦
// main.js
ReactDOM.render(
<Provider store = {store}>
<LoginPage />
</Provider>,
document.getElementById('app')
);
其實這跟使用App當跟組件的意義相同
只是我在跟組件就需要使用資料這樣
因為把狀態都交給唯一的 Store 了
所以 App 組件也變成函式型組件囉
最後只要修改與登入功能有關的地方就完成了
// App.jsx
function App({user, isLogin, setUser, login, logout}){
...
if(!isLogin){
...
}else{
return(
<div className="animateBg">
<div className="loginBlock">
<h1 style= {loginPrompt} >你好,先登入一下如何?</h1>
<form style={formStyle} onSubmit={login}>
<input id="user" type="text" placeholder="請輸入您的大名"
onChange={(event) => setUser(event)}/>
<input type="submit" value="確認" />
</form>
</div>
</div>
);
}
}
由於資料及行為函式都改為透過 connect 以參數型式傳入
所以再也不會看到this.state.x
這類字眼
全部都可以直接呼叫囉
這邊有讓我小小傷腦筋的是onChange={(event) => setUser(event)}
這裡本來我只想針對輸入的值傳遞
不過沒辦法即時取出傳遞
所以我就把整個事件傳遞進去
就像 React 本身寫 handleEventChange 相同
把 event 傳遞進去直接讓 Reducer 取出 value
// reducer.js
function isLogined(state = loginData, action){
switch(action.type){
case SET_USER:
return Object.assign({}, state, {
// action.user 是整個 input tag 產生的 event
user : action.user.target.value
});
...
}
}
最後登入按鈕的點選只需要判斷是不是真的有輸入東西
並且切換登入狀態isLogin
完成登入
// reducer.js
function isLogined(state = loginData, action){
switch(action.type){
...
case LOGIN:
if(state.user == ""){
window.alert("阿阿阿您忘記名字了阿阿阿!");
return state;
}else{
return Object.assign({}, state, {
isLogin : true
});
}
...
}
}
以上就是登入部分的完成過程
接著是前一天沒有解決的問題
其實在昨天晚上
有稍微想到可能可行的方法
所以今天是直接接續著嘗試實作
非常幸運的成功完成填入圖形的動作了
程式邏輯上是
利用二維陣列存放遊戲狀態
透過map
函式把索引值一併傳遞進去
並且在最後表格內的 div 標籤呼叫 action
// OX.jsx
function OX({playerMark, OXgame, isWinner, putMark, isGameEnd, playAgainOOXX}){
var textStyle = {
paddingLeft:'15px'
}
var i = 0;
return(
<div className="OX">
<div className="left">
<table>
<tbody>
{OXgame.map(
(OXgame, i) => <OXList game={OXgame} onClick={putMark} x={i} key={i} />
)}
</tbody>
</table>
</div>
<div className="right">
<h3>規則:</h3>
<p style={textStyle}>兩位玩家輪流點選九宮格,先連線,先勝利!</p>
<input type="button" onClick={playAgainOOXX} value="新遊戲" />
</div>
</div>
);
}
這是圈圈叉叉的主要組件
他負責產生遊戲的左右畫面
一樣是透過 Container 組件以參數型式傳遞資料和行為進來使用
並且透過 map 產生遊戲畫面
傳遞進去的資料有索引值和放入符號的 action
接著看內部真正產生每一格欄位的組件
// OXList.jsx
function OXList(props){
console.log(props.onClick);
var game = props.game;
var clickLeft = props.onClick;
var x = props.x;
var divStyle = {
width: '100%',
height: '100%',
cursor: 'pointer'
}
return(
<tr>
<td><div style={divStyle} onClick={() => props.onClick(x, 0)}>{game[0]}</div></td>
<td><div style={divStyle} onClick={() => props.onClick(x, 1)}>{game[1]}</div></td>
<td><div style={divStyle} onClick={() => props.onClick(x, 2)}>{game[2]}</div></td>
</tr>
);
}
一排因為有三格
所以我直接寫了三個欄位這樣
根據索引位置的不同
呼叫符號寫入的對應陣列索引位置就不同
Container 組件長這樣:
const mapStateToProps = (state) => {
return {
playerMark : state.OXGame.playerMark,
OXgame : state.OXGame.OXgame,
isWinner : state.OXGame.isWinner
}
}
const mapDispatchToProps = (dispatch) => {
return {
putMark: (x, y) => {
dispatch( putMark(x, y) )
},
isGameEnd: () => {
dispatch( isGameEnd() )
},
playAgainOOXX: () =>{
dispatch( playAgainOOXX() )
}
}
}
使用不同的 Reducer 所以跟前面登入功能的 Container 組件長得不太一樣
不過概念上相同
所以這邊就不贅述啦~
總之
今天完成了登入系統
解決了昨天的卡關問題
成果畫面:
感覺已經開始慢慢上手了
寫出來的那一刻真的是超級震撼
覺得應該趁勝追擊
明天繼續努力!!
- Eva Vue.js 30天隨身包
- Ben那些年,我們一起錯過的Javascript
- Ray激戰ReactJS 30天
Day28 end
by 瑞Ray ⸜(* ॑꒳ ॑* )⸝