Skip to content

Commit b61bda9

Browse files
committed
Edit more steps
1 parent da24d67 commit b61bda9

File tree

3 files changed

+49
-58
lines changed

3 files changed

+49
-58
lines changed

src/demo/game.23.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ function Game() {
4242
const [history, setHistory] = useState([
4343
{ squares: Array(9).fill(null) },
4444
])
45-
const [stepNumber, setStepNumber] = useState(0)
4645
const [xIsNext, setXIsNext] = useState(true)
46+
const [stepNumber, setStepNumber] = useState(0)
4747

48-
const current = history[history.length - 1]
48+
const current = history[stepNumber]
4949
const winner = calculateWinner(current.squares)
5050
let status = winner
5151
? "Winner: " + winner

src/demo/game.24.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ function Game() {
4242
const [history, setHistory] = useState([
4343
{ squares: Array(9).fill(null) },
4444
])
45-
const [stepNumber, setStepNumber] = useState(0)
4645
const [xIsNext, setXIsNext] = useState(true)
46+
const [stepNumber, setStepNumber] = useState(0)
4747

4848
const current = history[stepNumber]
4949
const winner = calculateWinner(current.squares)

src/tutorial.mdx

+46-55
Original file line numberDiff line numberDiff line change
@@ -380,8 +380,6 @@ Currently, each Square component maintains the game's state. To check for a winn
380380

381381
We may think that Board should just ask each Square for the Square's state. Although this approach is possible in React, we discourage it because the code becomes difficult to understand, susceptible to bugs, and hard to refactor. Instead, the best approach is to store the game's state in the parent Board component instead of in each Square. The Board component can tell each Square what to display by passing a prop, [just like we did when we passed a number to each Square](#passing-data-through-props).
382382

383-
**To collect data from multiple children, or to have two child components communicate with each other, you need to declare the shared state in their parent component instead. The parent component can pass the state back down to the children by using props; this keeps the child components in sync with each other and with the parent component.**
384-
385383
<Column>
386384
<Editor
387385
code="game.06.js"
@@ -399,20 +397,14 @@ We may think that Board should just ask each Square for the Square's state. Alth
399397
/>
400398
</Column>
401399

400+
**To collect data from multiple children, or to have two child components communicate with each other, you need to declare the shared state in their parent component instead. The parent component can pass the state back down to the children by using props; this keeps the child components in sync with each other and with the parent component.**
401+
402402
Lifting state into a parent component is common when React components are refactored -- let's take this opportunity to try it out.
403403

404-
Add a `useState` call to the Board and set a state value called `squares` to start out with an array of 9 nulls corresponding to the 9 squares.
404+
We add a `useState` call to the Board and set a state value called `squares` to start out with an array of 9 nulls corresponding to the 9 squares.
405405

406406
When we fill the board in later, the `squares` array will look something like `["O", null, "X", "X", "X", "O", "O", null, null]`.
407407

408-
The Board's `renderSquare` function currently looks like this:
409-
410-
```javascript
411-
function renderSquare(i) {
412-
return <Square value={i} />
413-
}
414-
```
415-
416408
<Column>
417409
<Editor
418410
code="game.07.js"
@@ -513,6 +505,8 @@ Here's a review of how this is achieved:
513505
>
514506
> The DOM `<button>` element's `onClick` attribute has a special meaning to React because it is a built-in component. For custom components like Square, the naming is up to you. We could give any name to the Square's `onClick` prop or Board's `handleClick` function, and the code would work the same. In React, it's conventional to use `on[Event]` names for props which represent events and `handle[Event]` for the functions which handle the events.
515507
508+
When we try to click a Square, we should get an error because we haven't defined `handleClick` yet.
509+
516510
<Column>
517511
<Editor
518512
code="game.09.js"
@@ -530,12 +524,12 @@ Here's a review of how this is achieved:
530524
/>
531525
</Column>
532526

533-
When we try to click a Square, we should get an error because we haven't defined `handleClick` yet. We'll now add `handleClick` to the Board class:
534-
535-
**[View the full code at this point](https://codepen.io/kickstartcoding/pen/LYpdBMz?editors=0011)**
527+
We'll now add `handleClick` to the Board class.
536528

537529
After these changes, we're again able to click on the Squares to fill them, the same as we had before. However, now the state is stored in the Board component instead of the individual Square components. When the Board's state changes, the Square components re-render automatically. Keeping the state of all squares in the Board component will allow it to determine the winner in the future.
538530

531+
**[View the full code at this point](https://codepen.io/kickstartcoding/pen/LYpdBMz?editors=0011)**
532+
539533
Since the Square components no longer maintain state, the Square components receive values from the Board component and inform the Board component when they're clicked. In React terms, the Square components are now **controlled components**. The Board has full control over them.
540534

541535
Note how in `handleClick`, we call `.slice()` to create a copy of the `squares` array to modify instead of modifying the existing array. We will explain why we create a copy of the `squares` array in the next section.
@@ -720,8 +714,8 @@ Now we need to decide which component should own the `history` state.
720714

721715
<Column>
722716
<Editor
723-
code="game.16.js"
724-
focus="61,62:65,77"
717+
code="game.17.js"
718+
focus="61,62:65,67:71,76,79"
725719
file="index.js"
726720
lang="jsx"
727721
height={450}
@@ -730,7 +724,7 @@ Now we need to decide which component should own the `history` state.
730724
<Browser
731725
style={{ height: "100%" }}
732726
height={200}
733-
loadUrl="/demo/16"
727+
loadUrl="/demo/17"
734728
id="browser"
735729
/>
736730
</Column>
@@ -741,26 +735,9 @@ We'll want the top-level Game component to display a list of past moves. It will
741735

742736
Placing the `history` state into the Game component lets us remove the `squares` state from its child Board component. Just like we ["lifted state up"](#lifting-state-up) from the Square component into the Board component, we are now lifting it up from the Board into the top-level Game component. This gives the Game component full control over the Board's data, and lets it instruct the Board to render previous turns from the `history`.
743737

744-
First, we'll set up the initial state for the Game component with `useState` calls.
745-
746-
<Column>
747-
<Editor
748-
code="game.17.js"
749-
focus="67:71,76,79"
750-
file="index.js"
751-
lang="jsx"
752-
height={450}
753-
id="editor"
754-
/>
755-
<Browser
756-
style={{ height: "100%" }}
757-
height={200}
758-
loadUrl="/demo/17"
759-
id="browser"
760-
/>
761-
</Column>
738+
First, we set up the initial state for the Game component with `useState` calls.
762739

763-
We'll update the Game component to use the most recent history entry to determine and display the game's status.
740+
We also update the Game component to use the most recent history entry to determine and display the game's status.
764741

765742
<Column>
766743
<Editor
@@ -802,7 +779,14 @@ Finally, we need to move the `handleClick` function from the Board component to
802779
/>
803780
</Column>
804781

805-
Next, we'll have the Board component receive `squares` and `onClick` props from the Game component. Since we now have a single click handler in Board for many Squares, we'll need to pass the location of each Square into the `onClick` handler to indicate which Square was clicked. Here are the required steps to transform the Board component:
782+
Next, we'll have the Board component receive `squares` and `onClick` props from the Game component.
783+
784+
We can now remove a few things from this component:
785+
786+
- The `useState` calls in Board.
787+
- The `handleClick` function.
788+
- The winner calculation.
789+
- The status.
806790

807791
<Column>
808792
<Editor
@@ -821,15 +805,9 @@ Next, we'll have the Board component receive `squares` and `onClick` props from
821805
/>
822806
</Column>
823807

824-
- Delete the `useState` calls in Board.
825-
- Replace `squares[i]` with `props.squares[i]` in Board's `renderSquare`.
826-
- Replace `handleClick(i)` with `props.onClick(i)` in Board's `renderSquare`.
827-
828-
The Board component now looks like this:
808+
Since we now have a single click handler in Board for many Squares, we'll need to pass the location of each Square into the `onClick` handler to indicate which Square was clicked.
829809

830-
Since the Game component is now rendering the game's status, we can remove the corresponding code from the Board. After refactoring, the Board's `return` looks like this:
831-
832-
At this point, the Board component only needs the `renderSquare` function. The game's state and the `handleClick` function should be in the Game component.
810+
At this point, the Board component only needs the `renderSquare` function. The game's state and the `handleClick` function are in the Game component.
833811

834812
**[View the full code at this point](https://codepen.io/kickstartcoding/pen/WNQzgJw?editors=0010)**
835813

@@ -882,8 +860,6 @@ const doubled = numbers.map(x => x * 2) // [2, 4, 6]
882860

883861
Using the `map` method, we can map our history of moves to React elements representing buttons on the screen, and display a list of buttons to "jump" to past moves. We `map` over the `history` in the Game.
884862

885-
**[View the full code at this point](https://codepen.io/kickstartcoding/pen/qBOoMJJ)**
886-
887863
For each move in the tic-tac-toe game's history, we create a list item `<li>` which contains a button `<button>`. The button has a `onClick` handler which calls a function called `jumpTo()`. We haven't implemented the `jumpTo()` function yet. For now, we should see a list of the moves that have occurred in the game and a warning in the developer tools console that says:
888864

889865
> Warning:
@@ -974,8 +950,8 @@ Clicking any of the list item's buttons throws an error because the `jumpTo` fun
974950

975951
<Column>
976952
<Editor
977-
code="game.24.js"
978-
focus="46,52:55,57:61,66:69,71"
953+
code="game.23.js"
954+
focus="44,46,52:55"
979955
file="index.js"
980956
lang="jsx"
981957
height={450}
@@ -984,22 +960,37 @@ Clicking any of the list item's buttons throws an error because the `jumpTo` fun
984960
<Browser
985961
style={{ height: "100%" }}
986962
height={200}
987-
loadUrl="/demo/24"
963+
loadUrl="/demo/23"
988964
id="browser"
989965
/>
990966
</Column>
991967

992-
Before we implement `jumpTo`, we'll add `stepNumber` to the Game component's state to indicate which step we're currently viewing.
968+
First we add `stepNumber` to the Game component's state to indicate which step we're currently viewing. We use `stepNumber:0` for the initial state in Game.
993969

994-
First, add `stepNumber: 0` to the initial state in Game:
970+
Next, we define the `jumpTo` function in Game to update that `stepNumber`. We also set `xIsNext` to true if the number that we're changing `stepNumber` to is even.
995971

996-
Next, we'll define the `jumpTo` function in Game to update that `stepNumber`. We also set `xIsNext` to true if the number that we're changing `stepNumber` to is even:
972+
<Column>
973+
<Editor
974+
code="game.24.js"
975+
focus="57:61,66:69,71"
976+
file="index.js"
977+
lang="jsx"
978+
height={450}
979+
id="editor"
980+
/>
981+
<Browser
982+
style={{ height: "100%" }}
983+
height={200}
984+
loadUrl="/demo/24"
985+
id="browser"
986+
/>
987+
</Column>
997988

998-
We will now make a few changes to the Game's `handleClick` function which fires when you click on a square.
989+
Now we make a few changes to the Game's `handleClick` function which fires when you click on a square.
999990

1000991
The `stepNumber` state we've added reflects the move displayed to the user now. After we make a new move, we need to update `stepNumber` by calling `setStepNumber(history.length)`. This ensures we don't get stuck showing the same move after a new one has been made.
1001992

1002-
We will also replace reading `history` with `history.slice(0, stepNumber + 1)`. This ensures that if we "go back in time" and then make a new move from that point, we throw away all the "future" history that would now become incorrect.
993+
We also replace reading `history` with `history.slice(0, stepNumber + 1)`. This ensures that if we "go back in time" and then make a new move from that point, we throw away all the "future" history that would now become incorrect.
1003994

1004995
If we click on any step in the game's history, the tic-tac-toe board should immediately update to show what the board looked like after that step occurred.
1005996

0 commit comments

Comments
 (0)