iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 30
0

前一日碰到的問題
查看了幾次程式碼
也到網路上去找找有沒有類似狀況
最後
沒有重新渲染的最根本原因是
改變到原本的狀態資料而不是真正回傳一個新的狀態資料
看到這具的時候覺得問號
因為我都是用Object.assign來修改資料並且產生新狀態回傳
於是嘗試了一下
發現是array.push導致沒有重新渲染
這時就奇怪啦
只是放進去新的紀錄
也有透過變數取出後再 push
為什麼卻導致系統不會自動刷新呢?
於是再就這個問題問估狗大神
看了一些資料後
原來
有一些事項是需要注意的

如果狀態資料有巢狀階層型態的資料時
資料的更新必須要
每一層都變動
如果只更新其中一層資料
並不會觸發重新渲染
換句話說使用push等等函式增加陣列索引和資料
並不能構成刷新的條件
另外要注意的是不能夠直接修改
定義變數並不會建立新的資料
只是指向原先的資料
因此定義一個新的變數再調整
也是針對原資料變動的行為
真的是個超級陷阱

那具體而言我們要怎麼做呢?
有一個方法可以實現這件事情
只要透過一個函式以參數形式傳入陣列資料
把陣列拆開後
透過slice放入要新增的資料或摘除要刪去的資料
最後回傳一個新的陣列
這樣就可以變成產生一個新的狀態資料了

// reducer.js
function insertResult(array, result){
    var i = array.length;
    return [
        ...array.slice(0,i),
        result
    ];
}

透過這樣回傳新的陣列資料後
再用Object.assign更新狀態
那原本該刷新的頁面就會正常運作啦!

解決前一日顯示的問題後
猜數字的遊戲基本上輸入、判斷、新遊戲都沒有問題了
只剩下一個就是
在最開始的時候
Store 持有的是預設資料而不是隨機資料
那麼每次第一場遊戲就會是固定的答案
除非點選新遊戲讓系統產生新的數字組合
因此要來使用
組件的 Lifecycle API 達成這件事情啦
只要在組件渲染出來之後
立刻初始化資料
那麼就會馬上置入一個新的隨機數字組合啦
也就能實現這個遊戲的全部了

這邊我把產生新數字組合寫成一個函式

function getNewNumber(){
    var abGame = [0,1,2,3,4,5,6,7,8,9];
    var i, j, swap, result;
    var k = Math.floor(Math.random()*6);  

    for(i=0; i<9; i++){
        j = Math.floor(Math.random()*9);
        swap = abGame[i];
        abGame[i] = abGame[j];
        abGame[j] = swap;   
    }
    result = abGame.slice(k, k+4);
    if( result[0] == 0 ){
        j = Math.floor(Math.random()*2+1);
        swap = result[0];
        result[0] = result[j];
        result[j] = swap;
    }
    return result;
}

由於規則有說明 0 不能在第一位
所以這邊多用了一層檢查
如果第一層是 0
那就再隨機與後面抽換一次
處理完操作的函式後
接著要使用生命週期函式
使用前
必須先把函式型組件改回繼承型
怎麼這麼任性阿T^T

這個更動主要影響的只有
參數要變為只有一個 props
也就是說所有取用的狀態資料和函式都要改寫為透過 props 存取

class AB extends React.Component{
    constructor(){
        super();
    }

    componentDidMount(){
        this.props.playAgainAABB();
    }

    render(){
        var textStyle = {
           paddingLeft:'15px'
        }

        var newGameStyle = {
            display:'inline-block',
            marginLeft:'40px'
        }

        return(
            <div className="AB">
                <div className="left">
                    <fieldset>
                        <legend>猜題紀錄</legend>
                        {this.props.History.map(
                           (History, i) => <ABList key={i} data={History} />
                           )}
                    </fieldset>
                </div>
                <div className="right">
                    <h3>規則:</h3>
                    <p style={textStyle}>數字對位置對為A</p>
                    <p style={textStyle}>數字對位置錯為B</p>
                    <p style={textStyle}>數字不會重複出現</p>
                    <p style={textStyle}>數字0不會出現在第一位</p>
                    <h3 style={{paddingTop:'5px'}}>猜題:</h3>
                    <input type="text" placeholder="請輸入4位不重複數字" 
                           onChange={(event) => this.props.saveInput(event)} maxLength="4"/>
                    <input type="button" value="確認" onClick={this.props.guessNum}/>
                    <input style={newGameStyle} type="button" value="新遊戲" onClick={this.props.playAgainAABB} />
                </div>
            </div>
        );
    }
}

繼承行函式內部就可以直接使用生命週期函式
componentDidMount中呼叫一次初始化的 Action
這麼一來就能夠在來到這個遊戲的頁面時
馬上就有一個新的題目可以玩囉~

成就清單

https://ithelp.ithome.com.tw/upload/images/20180118/20107674Ww8NH2j5MT.png

參考資料

  1. tutorialspoint-ReactJS Tutorial
  2. React 官方文件
  3. Immutable Update Patterns

>>> 隊友任意門 <<<


Day30 end
by 瑞Ray (((o(゚▽゚)o)))


上一篇
【Day29】 哎呀?二度卡關T^T
下一篇
鐵人賽30天 參賽心得
系列文
激戰 ReactJS 30天31

1 則留言

0
SunAllen
iT邦高手 1 級 ‧ 2018-01-18 17:40:58

恭賀完賽!!

謝謝 =D

我要留言

立即登入留言