發(fā)布于:2021-02-01 16:10:20
0
81
0
這是我第一次在博客上發(fā)表的關(guān)于學(xué)習(xí)React的文章的延續(xù)。我一直在努力學(xué)習(xí)reactjs.org,上一次,我在構(gòu)建一個(gè)基本的tic-tac-toe游戲方面取得了進(jìn)展。在這篇博文中,我將完成它?。ㄏM绱耍。?/span>
所以當(dāng)我們上次結(jié)束時(shí),我剛剛對(duì)用戶選擇正方形的能力進(jìn)行了編碼。但他們只能把方塊變成“X”-es,沒有任何機(jī)制讓任何人獲勝。顯然,我們還有很多事情要做:
好吧,那么。。。什么?這篇課文有點(diǎn)混亂。我認(rèn)為這是在說,我們不希望董事會(huì)必須不斷地查詢每個(gè)方塊的狀態(tài),以確定是否有人贏得了比賽。這聽起來像正方形將發(fā)送他們的狀態(tài)時(shí),他們更新(這應(yīng)該只發(fā)生一次)和董事會(huì)將保持跟蹤它從那一點(diǎn)。但是,就像我說的,我不確定,因?yàn)檫@段文字不是很清楚。
所以,這一節(jié)的標(biāo)題是“提升狀態(tài)”,這是我看到的下一段文字:
我不得不讀幾遍來解析它,但聽起來好像是說,每當(dāng)你想讓兩個(gè)組件互相對(duì)話時(shí),它們必須通過父組件來實(shí)現(xiàn)。我不知道為什么。
……或者這篇文章(和上一篇文章)是說這樣做是一種推薦的做法?是不是因?yàn)槿魏我粋€(gè)孩子都可以將自己的狀態(tài)傳遞給父母,任何父母都可以設(shè)置孩子的狀態(tài),但孩子不能通過父母與其他孩子交談?這就是為什么鼓勵(lì)“提升狀態(tài)”成為父母的原因嗎?
這里的一些解釋會(huì)非常有用。
我將此constructor
添加到Board
中,以將電路板的狀態(tài)初始化為九個(gè)空方塊:
constructor(props) { super(props); this.state = { squares: Array(9).fill(null) }; }
不過,在示例代碼中,從squares: Array...
開始的行的末尾有一個(gè)懸空的逗號(hào)。我去掉了這個(gè)懸垂的逗號(hào),我認(rèn)為這是一個(gè)打字錯(cuò)誤。
初始化this.state.squares
的語法與初始化單個(gè)方塊中的this.state.value
的語法相似:
this.state = { value: null };
this.state = { squares: Array(9).fill(null) };
…除此之外,我們沒有使用單個(gè)Square
中的單個(gè)value
,而是使用Array
的9
值,每個(gè)值默認(rèn)設(shè)置為null
。我想是吧。
我甚至不知道發(fā)生了什么,但我現(xiàn)在看到了,是的。這里:
renderSquare(i) { return <Square value={i} />; }
…當(dāng)我們渲染一個(gè)正方形時(shí),我們向它發(fā)送值i
,該值由它在網(wǎng)格中的位置決定:
<div className="board-row"> {this.renderSquare(0)} {this.renderSquare(1)} {this.renderSquare(2)} </div>
所以i = 1, 2, 3, ...
。但是Square
類中的實(shí)際render()
方法是:
render() { return ( <button className="square" onClick={() => this.setState({value: 'X'})}> {this.state.value} </button> ); }
它完全忽略傳遞給它的i
,它成為其state
的未使用部分:
constructor(props) { super(props); this.state = { value: null }; }
…并使用this.setState({value: 'X'})}
將值設(shè)置為X
,而不管傳遞給它的值是多少。假設(shè)下一步,我們將修復(fù)此行為,并允許將狀態(tài)設(shè)置為X
或O
,具體取決于傳遞給renderSquare()
的值。
由于我們?cè)?/span>Board.state.squares
中定義了電路板的狀態(tài)(我們將在將來更新),因此我們可以通過改變renderSquare()
方法將一個(gè)正方形的狀態(tài)(從該數(shù)組)傳遞給正方形:
renderSquare(i) { return <Square value={i} />; }
變成
renderSquare(i) { return <Square value={this.state.squares[i]} />; }
好的,既然游戲的狀態(tài)被保存在Board
中,任何特定的Square
都不能直接更新游戲狀態(tài),因?yàn)閷?duì)象不能直接編輯其他對(duì)象的狀態(tài)。下一部分有點(diǎn)復(fù)雜。
首先,如果Square
不再跟蹤游戲的狀態(tài),我們可以完全刪除constructor
,因?yàn)樗龅囊磺卸际窃O(shè)置Square
的狀態(tài):
class Square extends React.Component { constructor(props) { super(props); this.state = { value: null }; } render() { return ( <button className="square" onClick={() => this.setState({value: 'X'})}> {this.state.value} </button> ); } }
變成
class Square extends React.Component { render() { return ( <button className="square" onClick={() => this.setState({value: 'X'})}> {this.state.value} </button> ) ; } }
然后,我們將把一個(gè)函數(shù)從Board
傳遞到Square
,它告訴Square
如何處理點(diǎn)擊,所以
renderSquare(i) { return <Square value={this.state.squares[i]} />; }
變成
renderSquare(i) { return ( <Square value = {this.state.squares[i]} onClick = {() => this.handleClick(i)} /> ); }
為便于閱讀,行縮進(jìn),return
之后必須有一個(gè)()
圍繞其內(nèi)容。否則,JavaScript自動(dòng)插入分號(hào)可能會(huì)破壞代碼。(誰認(rèn)為這是個(gè)好主意?)
當(dāng)然,這意味著Square
也應(yīng)該更新。我們應(yīng)該在button
的定義中使用this.props.onClick()
而不是this.setState({value: 'X'})}
class Square extends React.Component { render() { return ( <button className="square" onClick={() => this.setState({value: 'X'})}> {this.state.value} </button> ); } }
變成
class Square extends React.Component { render() { return ( <button className="square" onClick={() => this.props.onClick()> {this.state.value} </button> ); } }
哦,當(dāng)然,this.state.value
應(yīng)該更改為this.props.value
,因?yàn)?/span>Square
的狀態(tài)將從Board
發(fā)送到Square
的props
:
class Square extends React.Component { render() { return ( <button className="square" onClick={() => this.props.onClick()> {this.state.value} </button> ); } }
變成
class Square extends React.Component { render() { return ( <button className="square" onClick={() => this.props.onClick()> {this.props.value} </button> ); } }
我仍然不明白這一切是如何結(jié)合在一起的,但我猜這個(gè)解釋正在進(jìn)行中。
哦,是的,看,在那兒。我再次在終端中運(yùn)行npm start
,等待代碼運(yùn)行的時(shí)間非常長(zhǎng)。(還有其他人有這個(gè)問題嗎?)當(dāng)它出現(xiàn)時(shí),我會(huì)在瀏覽器中看到一個(gè)錯(cuò)誤頁面:
我做了什么?
哦,看起來我忘了在我的代碼中將{this.state.value}
更新為{this.props.value}
,盡管我是在這里寫的。讓我們改變一下,再試一次:
太好了,成功了!它應(yīng)該以這種特定的方式崩潰,因?yàn)槲覀冞€沒有在this.props
中定義onClick()
函數(shù)。
所以在我有this.props.onClick()
的地方,我應(yīng)該換成this.props.handleClick()
。為了清楚起見,讓我在這里復(fù)制整個(gè)index.js
文件:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; class Square extends React.Component { render() { return ( <button className="square" onClick={() => this.props.handleClick()}> {this.props.value} </button> ); } } class Board extends React.Component { constructor(props) { super(props); this.state = { squares: Array(9).fill(null) }; } renderSquare(i) { return ( <Square value={this.state.squares[i]} onClick={() => this.handleClick(i)} />; ); } render() { const status = 'Next player: X'; return ( <div> <div className="status">{status}</div> <div className="board-row"> {this.renderSquare(0)} {this.renderSquare(1)} {this.renderSquare(2)} </div> <div className="board-row"> {this.renderSquare(3)} {this.renderSquare(4)} {this.renderSquare(5)} </div> <div className="board-row"> {this.renderSquare(6)} {this.renderSquare(7)} {this.renderSquare(8)} </div> </div> ); } } class Game extends React.Component { render() { return ( <div className="game"> <div className="game-board"> <Board /> </div> <div className="game-info"> <div>{/* status */}</div> <ol>{/* TODO */}</ol> </div> </div> ); } } // ======================================== ReactDOM.render( <Game />, document.getElementById('root') );
我還漏掉了代碼中的其他一些東西。在這里做筆記,并在終端中編輯代碼,同時(shí)閱讀教程可能會(huì)有點(diǎn)混亂。我認(rèn)為以上所有內(nèi)容都和教程中的一樣,所以讓我們繼續(xù)。
為了消除第二個(gè)錯(cuò)誤(“_this.props.onClick
不是函數(shù)”)并記住我們將onClick
重命名為handleClick
,我們現(xiàn)在必須在Board
中定義handleClick
方法:我不確定我真的從這個(gè)教程中學(xué)到了什么。不僅僅是復(fù)制和粘貼預(yù)先編寫的代碼。不過,我會(huì)堅(jiān)持到底。
在Board
中,我們現(xiàn)在定義handleClick()
方法:
handleClick(i) { const squares = this.state.squares.slice(); squares[i] = 'X'; this.setState({squares: squares}); }
在我讀之前,讓我看看我是否能猜出這是在做什么。首先,它是一個(gè)接受單個(gè)參數(shù)的函數(shù),這個(gè)參數(shù)是板上正方形的索引(無論是0-8
還是1-9
,我都不知道JavaScript是基于0
還是基于1
)。然后,它在方法中創(chuàng)建一個(gè)ant局部變量,并將其初始化為自己的state.squares
。如果squares
已經(jīng)是一個(gè)數(shù)組,我不知道為什么slice()
需要出現(xiàn)在那里。另外,當(dāng)我們?cè)谙乱恍兄懈钠湟粋€(gè)元素的值時(shí),為什么將squares
聲明為const
?最后,我們用setState
設(shè)置狀態(tài)。在JavaScript中,變量似乎是通過值傳遞的,因此我們必須顯式地將squares.state
的值復(fù)制到一個(gè)局部變量中,然后編輯該變量,然后將編輯后的變量傳遞回以更改狀態(tài)。有多少是對(duì)的?
……好吧,我想我以后會(huì)知道的。
這是字面上的下一段,開始解釋這一點(diǎn)。如果你下次再談的話,為什么還要說“我們稍后再解釋”?這就是為什么他們建議這樣做:
對(duì)我來說很自然的方法是直接編輯Square
的狀態(tài),但是教程推薦的方法是創(chuàng)建一個(gè)新對(duì)象,而不是改變現(xiàn)有對(duì)象。本教程建議盡可能保持對(duì)象不變,以便于檢測(cè)更改,并且應(yīng)用程序可以輕松恢復(fù)到以前的狀態(tài),以及其他好處。
天啊??梢?。也許是我,因?yàn)槲覜]有很強(qiáng)的JavaScript/反應(yīng)式前端編程背景,但本教程似乎從一個(gè)概念跳到了另一個(gè)概念,基本上沒有分段。似乎沒有一個(gè)明確的目標(biāo)或?qū)W習(xí)途徑。我覺得我只是在學(xué)習(xí)一個(gè)隨機(jī)的概念,然后復(fù)制一些代碼,然后繼續(xù)做下一件事。到目前為止還不怎么喜歡。
好吧,這看起來很簡(jiǎn)單。由于正方形本身不包含任何狀態(tài),因此它將通過在Board
中調(diào)用此函數(shù)來呈現(xiàn)。
好吧,但為什么。沒有解釋,我們繼續(xù)下一件事。
在更改Board
中的renderSquare()
函數(shù)之前,我們將添加在電路板上繪制O
以及X
es的功能。我們?cè)?/span>Board
“sconstructor
中設(shè)置初始狀態(tài):
class Board extends React.Component { constructor(props) { super(props); this.state = { squares: Array(9).fill(null) }; } }
變成
class Board extends React.Component { constructor(props) { super(props); this.state = { squares: Array(9).fill(null), xIsNext: true }; } }
同樣,在xIsNext: true
的末尾有一個(gè)懸空的逗號(hào),我已經(jīng)去掉了。這是故意的嗎?
所以xIsNext
是一個(gè)布爾值,我們每次渲染正方形時(shí)都會(huì)翻轉(zhuǎn)它。當(dāng)我們重寫renderSquare()
時(shí)(我想),我們將把xIsNext
從false翻轉(zhuǎn)到true,反之亦然,在我們決定繪制X
或O
之前,檢查xIsNext
的狀態(tài)。我們改變
handleClick(i) { const squares = this.state.squares.slice(); squares[i] = 'X'; this.setState({squares: squares}); }
至
handleClick(i) { const squares = this.state.squares.slice(); squares[i] = this.state.xIsNext ? 'X' : 'O'; this.setState({ squares: squares, xIsNext: !this.state.xIsNext }); }
哦,打字錯(cuò)誤。我來修一下。
快到了!正如你在上面看到的,游戲仍然沒有宣布勝出。我想這是下一步要做的事情,但在我們做之前,教程希望我們呈現(xiàn)一個(gè)消息,說輪到誰了。我們添加一行:
const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
…在Board
的render()
函數(shù)的頂部(現(xiàn)在,它總是說X
是下一個(gè)玩家):
我也注意到,當(dāng)我編輯index.js
文件時(shí),React會(huì)自動(dòng)重新呈現(xiàn)localhost:3000
處的頁面。那太好了!
好的,最后一件事最后一件事:我們?nèi)绾涡紕倮撸?/span>
我現(xiàn)在真的沒有精力了,所以我很高興這一節(jié)快結(jié)束了。
我更喜歡一個(gè)教程,從最小的可理解的代碼開始,從那里開始工作,而不是從一個(gè)骨架開始,一遍遍地說“好的,現(xiàn)在復(fù)制并粘貼這個(gè)內(nèi)容到那里”。
在無意中復(fù)制了更多的代碼之后。。。
本教程還有一節(jié),但我嚴(yán)重缺乏完成它的動(dòng)力。我想我想嘗試一個(gè)不同的教程或書,從基礎(chǔ)開始,并建立在他們的基礎(chǔ)上。
我要退出這個(gè)教程75%的方式通過。我覺得很沮喪,而且我覺得我實(shí)際上沒有學(xué)到多少反應(yīng)。也許是在反應(yīng)js.org我應(yīng)該考慮為本教程做一些焦點(diǎn)小組測(cè)試,因?yàn)槲掖_信我不是唯一一個(gè)有這種反應(yīng)的人。
作者介紹
熱門博客推薦