You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: src/tutorial.mdx
+46-55
Original file line number
Diff line number
Diff line change
@@ -380,8 +380,6 @@ Currently, each Square component maintains the game's state. To check for a winn
380
380
381
381
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).
382
382
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
-
385
383
<Column>
386
384
<Editor
387
385
code="game.06.js"
@@ -399,20 +397,14 @@ We may think that Board should just ask each Square for the Square's state. Alth
399
397
/>
400
398
</Column>
401
399
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
+
402
402
Lifting state into a parent component is common when React components are refactored -- let's take this opportunity to try it out.
403
403
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.
405
405
406
406
When we fill the board in later, the `squares` array will look something like `["O", null, "X", "X", "X", "O", "O", null, null]`.
407
407
408
-
The Board's `renderSquare` function currently looks like this:
409
-
410
-
```javascript
411
-
functionrenderSquare(i) {
412
-
return<Square value={i} />
413
-
}
414
-
```
415
-
416
408
<Column>
417
409
<Editor
418
410
code="game.07.js"
@@ -513,6 +505,8 @@ Here's a review of how this is achieved:
513
505
>
514
506
> 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.
515
507
508
+
When we try to click a Square, we should get an error because we haven't defined `handleClick` yet.
509
+
516
510
<Column>
517
511
<Editor
518
512
code="game.09.js"
@@ -530,12 +524,12 @@ Here's a review of how this is achieved:
530
524
/>
531
525
</Column>
532
526
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.
536
528
537
529
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.
538
530
531
+
**[View the full code at this point](https://codepen.io/kickstartcoding/pen/LYpdBMz?editors=0011)**
532
+
539
533
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.
540
534
541
535
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.
720
714
721
715
<Column>
722
716
<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"
725
719
file="index.js"
726
720
lang="jsx"
727
721
height={450}
@@ -730,7 +724,7 @@ Now we need to decide which component should own the `history` state.
730
724
<Browser
731
725
style={{ height: "100%" }}
732
726
height={200}
733
-
loadUrl="/demo/16"
727
+
loadUrl="/demo/17"
734
728
id="browser"
735
729
/>
736
730
</Column>
@@ -741,26 +735,9 @@ We'll want the top-level Game component to display a list of past moves. It will
741
735
742
736
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`.
743
737
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.
762
739
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.
764
741
765
742
<Column>
766
743
<Editor
@@ -802,7 +779,14 @@ Finally, we need to move the `handleClick` function from the Board component to
802
779
/>
803
780
</Column>
804
781
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.
806
790
807
791
<Column>
808
792
<Editor
@@ -821,15 +805,9 @@ Next, we'll have the Board component receive `squares` and `onClick` props from
821
805
/>
822
806
</Column>
823
807
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.
829
809
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.
833
811
834
812
**[View the full code at this point](https://codepen.io/kickstartcoding/pen/WNQzgJw?editors=0010)**
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.
884
862
885
-
**[View the full code at this point](https://codepen.io/kickstartcoding/pen/qBOoMJJ)**
886
-
887
863
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:
888
864
889
865
> Warning:
@@ -974,8 +950,8 @@ Clicking any of the list item's buttons throws an error because the `jumpTo` fun
974
950
975
951
<Column>
976
952
<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"
979
955
file="index.js"
980
956
lang="jsx"
981
957
height={450}
@@ -984,22 +960,37 @@ Clicking any of the list item's buttons throws an error because the `jumpTo` fun
984
960
<Browser
985
961
style={{ height: "100%" }}
986
962
height={200}
987
-
loadUrl="/demo/24"
963
+
loadUrl="/demo/23"
988
964
id="browser"
989
965
/>
990
966
</Column>
991
967
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.
993
969
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.
995
971
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>
997
988
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.
999
990
1000
991
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.
1001
992
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.
1003
994
1004
995
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.
0 commit comments