# data[].attributes.body
2022-05-12 - Day 1

# data[].attributes.body
JS進度11/113 10%了

# data[].attributes.body
今天開始學React JS框架
在Ｍac上安裝了react
序章開始

# data[].attributes.body
Class

- 類別敘述(class expressions)
- 類別宣告(class declarations)

# data[].attributes.body
基本功能
https://zh-hant.reactjs.org/tutorial/tutorial.html#declaring-a-winner

# data[].attributes.attachment_link.readable_text
你不需要有任何 React 的基礎知識就能使用這份學習指南。

在我們開始這份學習指南之前

在這份學習指南中，我們會練習做一個小遊戲。 也許你會很想跳過這份指南，因為你不是遊戲開發者 - 但請試著跟著做做看。 在這份學習指南中，你所學到的技術是你做任何 React 應用程式的基礎，掌握基礎後會讓你對 React 有更深入的了解。

Tip

這份學習指南是設計給那些喜歡 從做中學的人們。如果你比較喜歡從零開始學習概念的話，請參考我們的 逐步教學 。你可能會發現本篇指南和教學課程其實是相輔相成的。

這份指南分成以下幾個部分：

你不需要一次就完成所有的部分才能從這份學習指南中獲益。試著練習，越多越好 - 即使只有一兩個部分。

在這份學習指南中，我們會教你如何用 React 做一個互動式的圈圈叉叉小遊戲。

在這裡你可以看到我們將會做什麼：完成結果。如果你看不懂其中的程式碼，或是你對其中的語法不熟，請不要擔心！這份指南的目的就是要幫助你了解 React 及其語法。

我們建議你在往下看這份指南之前，先看看上面這個圈圈叉叉小遊戲。你會注意到遊戲格子的右邊有一個數字列表。這個列表將會列出遊戲中的所有動作的歷史，並在遊戲進行的同時更新。

當你稍微了解這個圈圈叉叉小遊戲是怎麼玩的之後，你就可以把它關掉了。在這份指南中，我們會從更簡單的模板開始。我們的下一步是進行設定以幫助你開始開發這個遊戲。

我們假設你對 HTML 和 JavaScript 有一定的熟悉度，但即使你的背景是另一種程式語言，你應該也能游刃有餘地理解這份指南。我們也假設你對程式語言的中的某些概念，如 function、object、array 以及（某種程度上）class，有一定的涉獵。

如果你需要複習 JavaScript，我們建議你閱讀這份 教學指南。請注意我們也會用到一些 ES6，也就是 JavaScript 最新的版本之一。在這份指南中，我們將會使用 arrow function、 class、，和 等表達式。你也可以用 Babel REPL 看看 ES6 的程式碼是如何被編譯的。

教學設定

完成這份指南有兩種方法：你可以在瀏覽器中寫程式碼，或在你的電腦裡建立一個本地的開發環境。

這是開始最快的方法！

首先，在瀏覽器中的分頁中打開這份 Starter Code。你應該會看到一個空白的圈圈叉叉遊戲格和一些 React 的程式碼。我們會在接下來的指南中，修改 React 的程式碼。

你現在可以跳過第二個選項，並前往 概論 的章節來了解 React 如何運作。

對完成這份指南來說，這完全是看你是否需要，而非必要的設定！

如果你卡住了，請參考 社群資源。其中 Reactiflux Chat 可以讓你很快得到幫助。如果沒人回覆，或者你依然無法解決，請填寫一個 issue，我們會提供協助。

概論

設定完成後，讓我們來討論 React 的概論吧！

React 是一個陳述式、高效且具有彈性的 JavaScript 函式庫，用以建立使用者介面。它讓你使用小巧而獨立的「component」，來建立複雜的 UI。

React 有數種不同的 component，但讓我們先從 React.Component 這個 subclass 開始談起：

我們稍後會解釋那些看起來很有趣像是 XML 的 tag。我們使用 component 告訴 React 我們想要在螢幕上看到什麼。當我們的資料改變時，React 將會很有效率地更新並重新 render 我們的 component。

在這裡，ShoppingList 是一個 React 的 component class，或 React component type。Component 會接受名為 props 的參數（「properties」的簡稱），並透過 render 這個方法回傳一個有階層架構的 view 到螢幕上。

render 方法回傳你想在螢幕上看到的 描述。React 接收這個描述並展示其結果。其中， render 回傳的是一個 React element，也就是一個 render 內容的輕量描述。大部分 React 的開發者會使用一種特殊的語法，被稱為「JSX」，因為它讓這些結構寫起來更容易。<div /> 語法在構建時被建立為 React.createElement('div') 。上述的例子其實也等同於以下這些程式碼：
你可以按這裡看到完整版
如果你想了解更多 createElement()，在 API 參考指南 中有更詳細的解釋，但我們不會在這份指南中用到它。我們會繼續使用 JSX。

JSX 就跟 JavaScript 一樣強大。你可以在 JSX 中的括號中放入 任何 JavaScript 的表達式。每個 React element 都是一個 JavaScript 的 object，你可以把它存在一個變數中或在程式中互相傳遞。

如果你打算在 你的瀏覽器中練習這份指南的話，請在分頁中開啟這份程式：Starter Code。如果你打算在 本地開發環境中練習，在你的專案資料夾中打開 src/index.js（你在 setup 時已接觸過這個檔案）。

這個 Starter Code 是我們接下來練習的基礎。我們已幫你準備好了 CSS，如此一來，你只需要把注意力集中在學習 React 和完成圈圈叉叉小遊戲的程式上。

請看看其中的程式。你會注意到我們有三個 React component：

Square component 會 render 一個按鈕 <button> ，而 Board 會 render 九個方格。Game component 則是 render 一個完整的遊戲格盤與 placeholder，這些值我們稍候會修改。目前為止，沒有一個 component 是互動式的。

做為暖身，讓我們試試看把一些資料從 Board component 傳給 Square component。

當你在練習這份指南時，我們建議你將程式碼自己寫過一遍，而不是複製貼上。這會幫助你訓練手感並加強理解。

在 Board 的 renderSquare 方法中，請修改程式碼，並把名為 value 的 prop 傳給 Square：

修改前：

修改後：你應該會看到每個方格中都有一個數字。

按這裡看目前的程式碼
恭喜！你剛剛已順利將把 prop 從 parent Board component 傳給 child Square component。傳遞 prop 是 React 的應用程式中資訊從 parent 傳給 children 的方式。

讓我們在點擊 Square component 時，能在方格中填入一個 X。 首先，把從 Square component 的 render() 中回傳的按鈕的標籤，修改成以下的程式：

現在，當你點擊任何一個 Square 時，我們應該能在瀏覽器中的 devtools 看到 console.log 的結果。

下一步，我們要讓 Square component 「記得」它被點擊了，並在方格中填入 X 這個記號。Component 使用 state 來保持狀態。

首先，我們要先加一個 constructor 在 class 中以初始化 state：

注意：

在 JavaScript classes 中，當你定義一個 subclass 的 constructor 時，你總是會需要呼叫 super。所有的 React component class，凡是有 constructor 的，都應該要從呼叫 super(props) 開始。

現在我們會改變 Square 的 render 方法以顯示當 Square 被點擊時當下的 state 的值是什麼：

把 <button> 標籤中的 this.props.value 換成 this.state.value。

把 onClick={...} 這個 event handler 換成 onClick={() => this.setState({value: 'X'})}。

將 className 和 onClick 兩個 prop 放在不同行，以方便閱讀。

在上述修改完成後，在 Square 的 render 方法中回傳的 <button> 標籤現在看起來是這樣：

藉由從 Square 的 render 方法中的 onClick handler 呼叫 this.setState，我們告訴 React：當該 Square 的 <button> 被點擊時，要重新 render。在這個修改後，Square 的 this.state.value 值將會變成 'X'，所以我們將會在遊戲格盤中看到 'X'。當你點擊任何一個方格，應該能看到 'X' 。

當你在一個 component 中呼叫 setState 時，React 也會自動更新其中的 child component。
按這裡看目前的程式碼
React 在 Chrome 和 Firefox 中的 Devtools extension 讓你使用你的瀏覽器中的開發者工具檢查 React component 的 tree。

React DevTools 讓你檢查你的 React component 中的 props 和 state。

在安裝 React DevTools 後，你在網頁上對任何元素按右鍵，之後按 Inspect 開啟開發者工具，React 的 tab 們（「⚛️ Components」和「⚛️ Profiler」）會是右邊數來第一個，便可使用「⚛️ Components」來檢查 component 的 tree。
然而，請注意在 CodePen 中使用開發者工具需要一些額外的步驟：

1. 登入網站，或註冊並確認你的 email (為防止垃圾郵件的必要手續)。

2. 點擊 Fork 按鈕。

3. 點擊 Change View 並選擇 Debug mode。

4. 在新開啟的分頁中，devtools 現在應該有 React 的 tab 了。

完成遊戲

現在我們已經為我們的圈圈叉叉遊戲準備好基本的要件了。為了要做出一個完整的遊戲，我們現在需要在遊戲格盤中交互地放入 X 和 O，我們也需要決定如何分出勝負。

目前，每個 Square component 都能更新這個遊戲的 state。如果要分出勝負的話，我們需要將這九個方格的值都紀錄在某處。

也許我們會覺得 Board 應該從每個 Square 中確認該方格的狀態。雖然這個方法在 React 中是可行的，但我們並不鼓勵你這麼做，因為你的程式碼會變得很難懂，很容易有 bug，也很難重寫。最好的方式是把這整個遊戲的 state 存放在 Board parent component 中，而不是在每一個 Square 中。Board component 會藉由傳遞 props 的方式告訴每一個 Square 該顯示什麼值， 就如同我們剛開始先傳給每個 Square 一個數字是一樣的 。
為了從多個 children 中收集資料，或是讓兩個 child component 互相溝通，你需要在它們的 parent component 裡宣告一個共享的 state。這個 parent component 可以將 state 透過 props 向下傳給 children。這讓 child component 之間還有跟它們的 parent component 能隨時保持同步。
在修改 React component 時，把 state 提升到 parent component 裡面是很常見的。讓我們利用這個機會來試試看這該怎麼做。我們會在 Board 裡加一個 constructor，並將 Board 的初始 state 設定為一個包含九個 null 的 array。這九個 null 分別對應著九個 Square：

當我們稍後填滿棋盤時，棋盤會看起來像這樣：

Board 的 renderSquare 方法目前看起來是這樣：

一開始，我們從 Board 把 value 這個 prop 往下傳，並在每一個 Square 中顯示數字 0 到 8。在之前的另一個步驟中，我們 根據 Square 本身的 state 把數字換成 Ｘ。這是為什麼 Square 目前會忽略 Board 傳給它的 value prop 的原因。

我們現在又會再次使用傳遞 prop 的這個機制。我們會修改 Board 以告訴每個 Square 它現在的值（'X'，'O' 或 null）該是什麼。我們已經在 Board 的 constructor 中定義了 squares 這個 array，接下來，我們會修改 Board 的 renderSquare 方法以讀取這個 array：
按這裡看目前的程式碼
接下來，我們需要改變當 Square 被點擊後會觸發的事件。Board component 現在決定了哪一個方格會被填滿。我們需要建立一個方法讓 Square 去更新 Board 的狀態。既然 state 對於定義它的 component 來說是 private 的，我們就不能直接從 Square 去更新 Board 的 state。

我們反而需要從 Board 傳一個 function 給 Square。這個 function 將在 Square 被點擊的時候被 Square 呼叫。我們也會改變 Board 中 renderSquare 的方法：

注意

為了方便閱讀，我們將回傳的 element 分成數行來寫，並加上括號，如此 JavaScript 才不會在 return 後加上一個分號並破壞我們的程式。

把 Square 的 render 方法中的 this.state.value 替換成 this.props.value。

把 Square 的 render 方法中的 this.setState() 替換成 this.props.onClick()。

把 constructor 從 Square 中刪除，因為 Square 已不再需要追蹤遊戲的狀態。

在上述修改完成後，Square component 現在看起來是這樣：

當一個 Square 被點擊時， onClick 這個 Board 所提供給它的 function 會被呼叫。我們來看一下這件事是如何做到的：

1. 在內建的 DOM <button> component 上的 onClick prop 告訴 React 要設定一個 click event listener。

2. 當按鈕被點擊時，React 會呼叫在 Square render() 方法中被定義的 onClick event handler。

3. 這個 event handler 將會呼叫 this.props.onClick()。Square 的 onClick prop 透過 Board 被指定。

4. 因為 Board 把 onClick={() => this.handleClick(i)} 傳給 Square，Square 會在被點擊時呼叫 handleClick(i)。

5. 我們尚未定義 handleClick()，所以我們的程式目前會崩潰。如果你現在點擊 Square 的話，將會看到「this.handleClick is not a function」之類的紅色錯誤訊息。

注意

DOM <button> element 的 onClick 屬性對 React 來說有特別的意義，因為它是一個內建的 component。對於像是 Square 這種客製的 components 來說，命名方式是看你的喜好。我們可以任意命名 Square 的 onClick prop 或是 Board 的 handleClick 方法，程式碼仍會正常運作。然而在 React 中，我們遵循的傳統通常是用 on[Event] 來命名那些代表 event 的 prop，用 handle[Event] 來命名那些 handle event 的方法。

當我們試著點擊一個 Square，我們的程式應該會發生錯誤，因為我們尚未定義 handleClick。我們現在把 handleClick 加到 Board 的 class：
按這裡看目前的程式碼
在這些修改完成後，我們現在又可以點擊 Squares 並填入內容了。然而，現在 state 是儲存在 Board component 而非個別的 Square component 中。當 Board 的 state 改變時，Square component 會自動重新 render。在 Board component 中維持所有方格的狀態將能使它在未來決定勝負。

因為 Square component 不再維持 state，Square component 從 Board component 接收 value 並在被點擊時通知 Board component 它的值。在 React 的詞彙中，Square component 現在是 被控制的 component 。這意味著 Board 對其有完全的掌握。

在上一段程式碼的範例中，我們建議你使用 .slice() 方法去建立一個 squares array 的副本，而不是修改已存在的 array。我們將會討論什麼是不可變性以及為什麼學習不可變性是很重要的。

一般來說，修改資料有兩種做法。第一種方法是透過改變資料的值來直接 修改 資料。第二種方法是改變 copy 中的資料，並用這個新的 copy 取代原本的資料。

透過 Mutation（修改）來改變資料

不透過 Mutation（修改）來改變資料

兩者的結果是相同的，但是藉由不直接修改資料（或直接更改底層資料），有下列幾個優點：

簡化複雜功能

不可變性使得複雜的功能變得更容易實現。稍後在這份教學指南中，我們將會實現「時間旅行」的功能。這個功能讓我們能回顧圈圈叉叉小遊戲的歷史並「跳回」之前的動作。這個功能並非只適用於遊戲 - 復原動作與取消復原動作的功能是應用程式中很常見的需求。避免直接修改資料讓我們能將遊戲歷史先前的版本完整的保留下來，並在之後重新使用它們。

偵測改變

在可變更的 object 中偵測改變是很困難的，因為這些改變是直接的。如果要偵測改變的話，我們需要比較這個可變更的 object 和它之前的 copy，並且走訪整個 object tree。

相較之下，在不可變更的 object 中偵測改變就容易多了。如果某個不可變更 object 和之前不一樣，那麼這個 object 就已經被改變了。

在 React 中決定何時重新 render

不可變性最主要的優點在於它幫助你在 React 中建立 pure component 。我們能很容易決定不可變的資料中是否有任何改變，這幫助 React 決定某個 component 是否需要重新 render。

在 效能最佳化中，你可以深入了解 shouldComponentUpdate() 以及如何建立 pure component 。

現在，我們來把 Square 改成一個 function component 。

在 React 中，當我們要寫只包含 render 方法且沒有自己 state 的 component 時， function component 是一種很簡易的寫法。與其定義一個 class 並延伸 React.Component，我們可以寫用 props 作為輸入並回傳 render 的 function。相較於 class 來說，function component 寫起來比較沒有那麼乏味。許多的 component 都能以這種形式表達。

把 Square class 換成這個 function：

我們把出現過兩次的 this.props 通通換成 props 。
按這裡看目前的程式碼

注意

當我們把 Square 變成 function component 的時候，我們也把 onClick={() => this.props.onClick()} 替換為更簡短的 onClick={props.onClick}（請特別注意在箭頭的 兩側 ，原本的括號現在都不見了）。

接下來，我們需要修正我們圈圈叉叉小遊戲中一個很明顯的缺陷：「O」沒辦法被放在棋盤上。

我們會將第一步的預設值設定為「X」。我們可以透過修改 Board constructor 內最初的 state 來設定這個預設值：

這個改變會讓「X」和「O」輪流出現，試試看吧！

我們也來更新一下 Board 的 render 中「status」的文字，讓它能顯示下一個玩家是誰：

在這些改變都完成後，你的 Board component 看起來應該會是這個樣子：
按這裡看目前的程式碼
在我們能顯示下一次輪到哪個玩家之後，我們也應該能在勝負揭曉時宣布誰是贏家，並告知玩家接下來沒有動作可出。在這個檔案的最後複製貼上這個 helper function：

現在我們可以改變 Board 裡面的 handleClick function。如果勝負已經揭曉，或者某個 Square 已經被填滿了，這個 function 可以透過忽略點擊的方式來提早回傳。
按這裡看目前的程式碼
恭喜！你現在有一個可行的圈圈叉叉小遊戲了。你也學到了 React 的基礎。所以，也許 你 才是真正的贏家。

加上時間旅行

作為最後的練習，讓我們試著在這個遊戲中把「回到過去的動作」變成可能。

如果我們修改 squares array 的話，時間旅行將會變得非常難以實現。

然而，在每一個動作之後，我們使用了 slice() 來建立一個 squares array 的新的 copy，並 把它視為是不可變的。這讓我們能夠儲存每一個 squares array 過去的版本，並悠遊於這些已經發生的動作之中。

現在我們需要決定哪一個 component 應該擁有 history 的 state。

我們會希望最頂層的 Game component 能展示過去一系列的動作。它需要能讀取 history 才能做到如此，所以我們會把 history state 放在最上層的 Game component 裡面。

把 history state 放在 Game component 裡面也讓我們能夠把 squares 的 state 從它的 child Board component 中移除。如同我們把 state 從 Square component 「提升」到 Board component 一樣，我們現在也要把 state 從 Board 再度提升到最頂層的 Game component 中。這讓 Game component 能完全掌握 Board 的資料，並讓它能告訴 Board 何時該從 history 中 render 之前的動作。

首先，我們會先在 Game component 的 constructor 中設定最初的 state：

在 Board 中刪除 constructor。

在 Board 的 renderSquare 裡把 this.state.squares[i] 換成 this.props.squares[i]。

在 Board 的 renderSquare 裡把 this.handleClick(i) 換成 this.props.onClick(i)。

現在，Board component 看起來是這樣：

我們將會更新 Game component 的 render function 以使用歷史上最新的紀錄並顯示遊戲的狀態：

既然 Game component 現在能 render 遊戲的狀態，我們可以把 Board 的 render 中相對應的程式碼移除。在修改之後，Board 的 render function 看起來是這樣子：

注意

concat()方法與你可能所熟悉的 array push() 方法不同，它並不會改變原本的 array，所以我們偏好這個方法。

按這裡看目前的程式碼
既然我們記錄了圈圈叉叉小遊戲的歷史，我們現在可以向玩家展示過去一系列的動作。

之前，我們學到 React 是 first-class JavaScript object。我們可以將這些 object 在我們的應用程式中傳遞。若是要在 React 中 render 數個項目，我們可以使用一個 array 的 React element。

在 JavaScript 中，array 有一個 常被用來對比並轉變資料，例如：

我們可以使用 map 方法將我們的歷史動作對比螢幕上代表按鈕的 React element，並展示一系列的按鈕以「跳回」過去的動作。
按這裡看目前的程式碼
當我們 interate history array 時， step 變數指的是目前的 history element 的值，而 move 指的是目前 history element 的 index。在這裡我們只對 move 有興趣，因此 step 沒有被分配到任何的東西。

在圈圈叉叉小遊戲歷史中的每個動作，我們都會建立一個列表元素 <li>，並在裡面加入 <button>。這個按鈕有一個會呼叫 this.jumpTo() 方法的 onClick handler。我們還沒實作 jumpTo() 方法。現在，我們應該能在開發者工具的 console 中看到一系列在遊戲中曾經發生過的動作以及一個警告：

警告： 每一個 array 或 iterator 中的 child 必須要有一個獨特的「key」屬性。請參考 Game 的 render 方法。

我們來討論上述這個警告的意義為何。

當我們 render 一個列表時，React 會儲存每一個被 render 的項目的資訊。當我們更新列表時，React 需要決定什麼被改變了。我們可以增加、移除、重新排序或更新列表中的項目。

想像一下從這個

變成這個

除了數量的更新之外，如果讓一個人類來閱讀這個列表的話，他可你會說我們把 Alexa 和 Ben 的順序調換了，並把 Claudia 插入 Alexa 和 Ben 之中。然而，React 是一個電腦程式，它並不知道我們的意圖。正因為 React 並不知道我們的意圖，我們需要明確的為每一個列表中的項目加入一個 key 的屬性，以確保 React 能清楚分辨每一個項目。一個可行的做法是使用 alexa、 ben、 claudia 等 string。如果我們是從資料庫來展示資料的話，Alexa、Ben 和 Claudia 的資料庫 ID 可以被用來當作 key。

{user.name}: {user.taskCount} tasks left

當一個列表被重新 render 的時候，React 會依據每一個列表項目的 key 搜尋上一個列表內的所有項目，並尋找相對應的 key。如果目前的列表有一個之前不存在的 key，那麼 React 就會建立一個 component。如果目前的列表缺少一個之前的列表中有的 key，React 便會將上一個 component 摧毀。如果兩個 key 符合，那麼相對應的 component 就會被移動。Key 告訴 React 每個 component 的身份，這讓 React 能夠在每次重新 render 之間維持 state。如果一個 component 的 key 改變了，那麼這個 component 就會被摧毀，然後在被加上新的 state 後重新被建立。

Key 是在 React 中一個特別的且被保留為關鍵字的屬性（跟 ref 這個較為進階的功能一樣）。當一個元素被建立時，React 會取出 key 屬性，並直接將這個 key 儲存在回傳的 element 內。雖然 key 可能看起來跟 props 像是同一類的關鍵字，但我們沒辦法使用 this.props.key 來參考 key。React 會自動使用 key 來決定哪一個 component 需要被更新。一個 component 無法得知自身的 key 是什麼。

我們極力推薦你在建立動態列表時一律指定適當的 key。 如果你沒有好好指定 key 的話，你可能要考慮重塑你的資料結構，讓你能夠使用 key。

如果 key 沒有被指定的話，React 會提出警告並使用 array 的索引作為 key 的預設值。但是使用 array 的索引作為 key 在重新排序一個列表中的項目或插入/移除項目時會有問題。明確的指定 key={i} 雖然能避開警告，卻還是會跟使用 array 的索引一樣產生同樣的問題。在大多數的情況下，我們不建議你這麼做。

Key 不需要是全域內獨特的值。它們只需要是在 component 和它們的 sibling 間是獨特的即可。

在圈圈叉叉小遊戲的歷史中，每個過去的動作都有一個獨特的 ID。這些是動作的序號。這些動作永遠不會被重新排序、刪除、或插入，所以我們可以放心使用每個動作的索引作為 key。

在 Game component 的 render 方法中，我們可以加入 <li key={move}> 作為我們的 key。如此一來 React 的警告應該就會消失了：
按這裡看目前的程式碼
首先，在 Game 的 constructor 裡面的初始 state 加入 stepNumber: 0 ：

請注意在 jumpTo 方法中，我們沒有更新 history 的狀態，這是因為狀態的更新會被合併，簡單來說 React 只會更新 setState 方法中提到的屬性，而其餘的狀態則保持不變。更多資訊請參考 。

現在，我們會在 Game 中每當一個方格被點擊就被觸發的 handleClick 中做一些修改。

我們也會將 this.state.history 替換成 this.state.history.slice(0, this.state.stepNumber + 1) 。假使我們「回到過去」的某個時刻，並做了一個跟過去不一樣的新動作，這將會確保我們會刪除從那一刻起所有屬於「未來」、但現在已不再正確的的歷史。

最後，我們會修改 Game component 中的 render 方法，從總是 render 最後一個動作，改為根據 stepNumber 來 render 目前正在被選取的動作：

現在，當我們點擊遊戲歷史中的任何一步，我們應該能夠立刻看到圈圈叉叉遊戲棋盤被更新，並顯示棋盤在那一步發生後的模樣。
按這裡看目前的程式碼
恭喜！你現在已經建立了一個圈圈叉叉遊戲，它可以：

讓你玩圈圈叉叉

顯示哪一個玩家取得勝利

在遊戲進行的同時儲存遊戲歷史

讓玩家回顧遊戲的歷史，並回到棋盤之前的版本

做得好！希望現在你覺得你對 React 的運作有相當程度的理解。

按這裡看看最終的結果：完成結果。

如果你有額外的時間或想練習你的 React 新技巧，下面是一些你可以改進圈圈叉叉小遊戲的想法，依照程度逐漸增加困難度：

1. 在歷史動作列表中，用（欄，列）的格式來顯示每個動作的位置。

2. 在動作列表中，將目前被選取的項目加粗。

3. 改寫 Board，使用兩個 loop 建立方格而不是寫死它。

4. 加上一個切換按鈕讓你可以根據每個動作由小到大、由大到小來排序。

5. 當勝負揭曉時，把連成一條線的那三個方格凸顯出來。

6. 當沒有勝負時，顯示遊戲結果為平手。

在這份教學指南中，我們討論了許多 React 的概念，如 element、component、prop 和 state。如果你想更深入了解這些概念的話，請參考 接下來的官方手冊。若想深入了解如何定義 component，請看 React.Component API 參考指南。

# data[].attributes.attachment_link.description
你不需要有任何 React 的基礎知識就能使用這份學習指南。 在這份學習指南中，我們會練習做一個小遊戲。 也許你會很想跳過這份指南，因為你不是遊戲開發者 - 但請試著跟著做做看。 在這份學習指南中，你所學到的技術是你做任何 React 應用程式的基礎，掌握基礎後會讓你對 React 有更深入的了解。 Tip 這份學習指南是設計給那些喜歡 從做中學的人們。如果你比較喜歡從零開始學習概念的話，請參考我們的 逐步教學 。你可能會發現本篇指南和教學課程其實是相輔相成的。 這份指南分成以下幾個部分： 你不需要一次就完成所有的部分才能從這份學習指南中獲益。試著練習，越多越好 - 即使只有一兩個部分。 在這份學習指南中，我們會教你如何用 React 做一個互動式的圈圈叉叉小遊戲。 在這裡你可以看到我們將會做什麼：完成結果 。如果你看不懂其中的程式碼，或是你對其中的語法不熟，請不要擔心！這份指南的目的就是要幫助你了解 React 及其語法。 我們建議你在往下看這份指南之前，先看看上面這個圈圈叉叉小遊戲。你會注意到遊戲格子的右邊有一個數字列表。這個列表將會列出遊戲中的所有動作的歷史，並在遊戲進行的同時更新。 當你稍微了解這個圈圈叉叉小遊戲是怎麼玩的之後，你就可以把它關掉了。在這份指南中，我們會從更簡單的模板開始。我們的下一步是進行設定以幫助你開始開發這個遊戲。 我們假設你對 HTML 和 JavaScript 有一定的熟悉度，但即使你的背景是另一種程式語言，你應該也能游刃有餘地理解這份指南。我們也假設你對程式語言的中的某些概念，如 function、object、array 以及（某種程度上）class，有一定的涉獵。 如果你需要複習 JavaScript，我們建議你閱讀這份 教學指南。請注意我們也會用到一些 ES6，也就是 JavaScript 最新的版本之一。在這份指南中，我們將會使用 arrow function、 class、，和 等表達式。你也可以用 Babel REPL 看看

# data[].attributes.body
一次搞懂CSS Flex

外層 - 主軸

justify-content : 主軸線對齊

flex-direction : 主軸線方向

flex-wrap : 換行方式

align-items : 整體交錯軸對齊

內層 -

flex

align-self : 內元素交錯軸對齊

# data[].attributes.attachment_link.title
CSS Flex 超詳解，彈性版型任你操控！