@@ -123,7 +123,10 @@ function Board({ xIsNext, squares, onPlay }) {
123
123
}
124
124
125
125
export default function Game () {
126
- const { history , setHistory , currentMove , setCurrentMove } = useGameStore ()
126
+ const history = useGameStore ((state ) => state .history )
127
+ const setHistory = useGameStore ((state ) => state .setHistory )
128
+ const currentMove = useGameStore ((state ) => state .currentMove )
129
+ const setCurrentMove = useGameStore ((state ) => state .setCurrentMove )
127
130
const xIsNext = currentMove % 2 === 0
128
131
const currentSquares = history[currentMove]
129
132
@@ -215,7 +218,7 @@ The `Square` component should take `value` and `onSquareClick` as props. It shou
215
218
216
219
Here's the code for the ` Square ` component:
217
220
218
- ``` tsx
221
+ ``` jsx
219
222
function Square ({ value, onSquareClick }) {
220
223
return (
221
224
< button
@@ -253,7 +256,7 @@ interactions.
253
256
254
257
Here's the code for the ` Board ` component:
255
258
256
- ``` tsx
259
+ ``` jsx
257
260
export default function Board () {
258
261
return (
259
262
< div
@@ -307,7 +310,7 @@ parent `Board` component instead of in each `Square` component. The `Board` comp
307
310
Let's take this opportunity to try it out. Edit the ` Board ` component so that it declares a state
308
311
variable named squares that defaults to an array of 9 nulls corresponding to the 9 squares:
309
312
310
- ``` tsx
313
+ ``` jsx
311
314
import { create } from ' zustand'
312
315
import { combine } from ' zustand/middleware'
313
316
@@ -354,7 +357,7 @@ export default function Board() {
354
357
array corresponds to the value of a square. When you fill the board in later, the squares array
355
358
will look like this:
356
359
357
- ``` ts
360
+ ``` js
358
361
const squares = [' O' , null , ' X' , ' X' , ' X' , ' O' , ' O' , null , null ]
359
362
```
360
363
@@ -374,7 +377,7 @@ Now you'll connect the `onSquareClick` prop to a function in the `Board` compone
374
377
` handleClick ` . To connect ` onSquareClick ` to ` handleClick ` you'll pass an inline function to the
375
378
` onSquareClick ` prop of the first Square component:
376
379
377
- ``` tsx
380
+ ``` jsx
378
381
< Square key= {squareIndex} value= {square} onSquareClick= {() => handleClick (i)} / >
379
382
```
380
383
@@ -385,12 +388,10 @@ The `handleClick` function should take the index of the square to update and cre
385
388
` squares ` array (` nextSquares ` ). Then, ` handleClick ` updates the ` nextSquares ` array by adding ` X `
386
389
to the square at the specified index (` i ` ) if is not already filled.
387
390
388
- ``` tsx {7-12,29 }
391
+ ``` jsx {5-10,27 }
389
392
export default function Board () {
390
- const [squares, setSquares] = useGameStore ((state ) => [
391
- state .squares ,
392
- state .setSquares ,
393
- ])
393
+ const squares = useGameStore ((state ) => state .squares )
394
+ const setSquares = useGameStore ((state ) => state .setSquares )
394
395
395
396
function handleClick (i ) {
396
397
if (squares[i]) return
@@ -434,7 +435,7 @@ board.
434
435
You'll set the first move to be ` 'X' ` by default. Let's keep track of this by adding another piece
435
436
of state to the ` useGameStore ` hook:
436
437
437
- ``` tsx {2,12-18}
438
+ ``` jsx {2,12-18}
438
439
const useGameStore = create (
439
440
combine ({ squares: Array (9 ).fill (null ), xIsNext: true }, (set ) => {
440
441
return {
@@ -463,16 +464,12 @@ Each time a player moves, `xIsNext` (a boolean) will be flipped to determine whi
463
464
and the game's state will be saved. You'll update the Board's ` handleClick ` function to flip the
464
465
value of ` xIsNext ` :
465
466
466
- ``` tsx {2-5,10,15 }
467
+ ``` jsx {2-3,6,11 }
467
468
export default function Board () {
468
- const [xIsNext, setXIsNext] = useGameStore ((state ) => [
469
- state .xIsNext ,
470
- state .setXIsNext ,
471
- ])
472
- const [squares, setSquares] = useGameStore ((state ) => [
473
- state .squares ,
474
- state .setSquares ,
475
- ])
469
+ const xIsNext = useGameStore ((state ) => state .xIsNext )
470
+ const setXIsNext = useGameStore ((state ) => state .setXIsNext )
471
+ const squares = useGameStore ((state ) => state .squares )
472
+ const setSquares = useGameStore ((state ) => state .setSquares )
476
473
const player = xIsNext ? ' X' : ' O'
477
474
478
475
function handleClick (i ) {
@@ -516,7 +513,7 @@ same array, checks for remaining turns by filtering out only `null` items, and r
516
513
them. The last helper called ` calculateStatus ` that takes the remaining turns, the winner, and the
517
514
current player (` 'X' or 'O' ` ):
518
515
519
- ``` ts
516
+ ``` js
520
517
function calculateWinner (squares ) {
521
518
const lines = [
522
519
[0 , 1 , 2 ],
@@ -555,7 +552,7 @@ function to check if a player has won. You can perform this check at the same ti
555
552
user has clicked a square that already has a ` 'X' ` or and ` 'O' ` . We'd like to return early in
556
553
both cases:
557
554
558
- ``` ts {2}
555
+ ``` js {2}
559
556
function handleClick (i ) {
560
557
if (squares[i] || winner) return
561
558
const nextSquares = squares .slice ()
@@ -570,16 +567,12 @@ To let the players know when the game is over, you can display text such as `'Wi
570
567
display the winner or draw if the game is over and if the game is ongoing you' ll display which
571
568
player' s turn is next:
572
569
573
- ``` tsx {10-11,13,25 }
570
+ ` ` ` jsx {6-7,9,21 }
574
571
export default function Board() {
575
- const [xIsNext, setXIsNext] = useGameStore ((state ) => [
576
- state .xIsNext ,
577
- state .setXIsNext ,
578
- ])
579
- const [squares, setSquares] = useGameStore ((state ) => [
580
- state .squares ,
581
- state .setSquares ,
582
- ])
572
+ const xIsNext = useGameStore((state) => state.xIsNext)
573
+ const setXIsNext = useGameStore((state) => state.setXIsNext)
574
+ const squares = useGameStore((state) => state.squares)
575
+ const setSquares = useGameStore((state) => state.setSquares)
583
576
const winner = calculateWinner(squares)
584
577
const turns = calculateTurns(squares)
585
578
const player = xIsNext ? 'X' : 'O'
@@ -622,7 +615,7 @@ export default function Board() {
622
615
Congratulations! You now have a working tic- tac- toe game . And you' ve just learned the basics of
623
616
React and Zustand too. So you are the real winner here. Here is what the code should look like:
624
617
625
- ``` tsx
618
+ ```jsx
626
619
import { create } from ' zustand'
627
620
import { combine } from ' zustand/ middleware'
628
621
@@ -672,14 +665,10 @@ function Square({ value, onSquareClick }) {
672
665
}
673
666
674
667
export default function Board () {
675
- const [xIsNext, setXIsNext] = useGameStore ((state ) => [
676
- state .xIsNext ,
677
- state .setXIsNext ,
678
- ])
679
- const [squares, setSquares] = useGameStore ((state ) => [
680
- state .squares ,
681
- state .setSquares ,
682
- ])
668
+ const xIsNext = useGameStore ((state ) => state .xIsNext )
669
+ const setXIsNext = useGameStore ((state ) => state .setXIsNext )
670
+ const squares = useGameStore ((state ) => state .squares )
671
+ const setSquares = useGameStore ((state ) => state .setSquares )
683
672
const winner = calculateWinner (squares)
684
673
const turns = calculateTurns (squares)
685
674
const player = xIsNext ? ' X' : ' O'
@@ -765,7 +754,7 @@ You'll keep track of these past squares arrays in a new state variable called `h
765
754
` history ` array will store all board states, from the first move to the latest one, and will look
766
755
something like this:
767
756
768
- ``` ts
757
+ ``` js
769
758
const history = [
770
759
// First move
771
760
[null , null , null , null , null , null , null , null , null ],
@@ -793,16 +782,12 @@ component data and instruct the `Board` component to render previous turns from
793
782
First, add a ` Game ` component with ` export default ` and remove it from ` Board ` component. Here is
794
783
what the code should look like:
795
784
796
- ``` tsx {1,48-65 }
785
+ ``` jsx {1,44-61 }
797
786
function Board () {
798
- const [xIsNext, setXIsNext] = useGameStore ((state ) => [
799
- state .xIsNext ,
800
- state .setXIsNext ,
801
- ])
802
- const [squares, setSquares] = useGameStore ((state ) => [
803
- state .squares ,
804
- state .setSquares ,
805
- ])
787
+ const xIsNext = useGameStore ((state ) => state .xIsNext )
788
+ const setXIsNext = useGameStore ((state ) => state .setXIsNext )
789
+ const squares = useGameStore ((state ) => state .squares )
790
+ const setSquares = useGameStore ((state ) => state .setSquares )
806
791
const winner = calculateWinner (squares)
807
792
const turns = calculateTurns (squares)
808
793
const player = xIsNext ? ' X' : ' O'
@@ -854,7 +839,7 @@ export default function Game() {
854
839
< Board / >
855
840
< / div>
856
841
< div style= {{ marginLeft: ' 1rem' }}>
857
- <ol >{ /* TODO*/ } </ol >
842
+ < ol> {/* TODO */ }< / ol>
858
843
< / div>
859
844
< / div>
860
845
)
@@ -863,7 +848,7 @@ export default function Game() {
863
848
864
849
Add some state to the ` useGameStore ` hook to track the history of moves:
865
850
866
- ``` ts {2,4-11}
851
+ ``` js {2,4-11}
867
852
const useGameStore = create (
868
853
combine ({ history: [Array (9 ).fill (null )], xIsNext: true }, (set ) => {
869
854
return {
@@ -895,9 +880,12 @@ To render the squares for the current move, you'll need to read the most recent
895
880
the ` history ` state. You don't need an extra state for this because you already have enough
896
881
information to calculate it during rendering:
897
882
898
- ``` tsx {2-3 }
883
+ ``` jsx {2-6 }
899
884
export default function Game () {
900
- const { history, setHistory, xIsNext, setXIsNext } = useGameStore ()
885
+ const history = useGameStore ((state ) => state .history )
886
+ const setHistory = useGameStore ((state ) => state .setHistory )
887
+ const xIsNext = useGameStore ((state ) => state .xIsNext )
888
+ const setXIsNext = useGameStore ((state ) => state .setXIsNext )
901
889
const currentSquares = history[history .length - 1 ]
902
890
903
891
return (
@@ -923,9 +911,12 @@ Next, create a `handlePlay` function inside the `Game` component that will be ca
923
911
component to update the game. Pass ` xIsNext ` , ` currentSquares ` and ` handlePlay ` as props to the
924
912
` Board ` component:
925
913
926
- ``` tsx {5-7,18 }
914
+ ``` jsx {8-10,21 }
927
915
export default function Game () {
928
- const { history, setHistory, xIsNext, setXIsNext } = useGameStore ()
916
+ const history = useGameStore ((state ) => state .history )
917
+ const setHistory = useGameStore ((state ) => state .setHistory )
918
+ const currentMove = useGameStore ((state ) => state .currentMove )
919
+ const setCurrentMove = useGameStore ((state ) => state .setCurrentMove )
929
920
const currentSquares = history[history .length - 1 ]
930
921
931
922
function handlePlay (nextSquares ) {
@@ -955,7 +946,7 @@ Let's make the `Board` component fully controlled by the props it receives. To d
955
946
the ` Board ` component to accept three props: ` xIsNext ` , ` squares ` , and a new ` onPlay ` function that
956
947
the ` Board ` component can call with the updated squares array when a player makes a move.
957
948
958
- ``` tsx {1}
949
+ ``` jsx {1}
959
950
function Board ({ xIsNext, squares, onPlay }) {
960
951
const winner = calculateWinner (squares)
961
952
const turns = calculateTurns (squares)
@@ -1008,7 +999,7 @@ squares array as a new `history` entry. You also need to toggle `xIsNext`, just
1008
999
component used
1009
1000
to do.
1010
1001
1011
- ``` ts {2-3}
1002
+ ``` js {2-3}
1012
1003
function handlePlay (nextSquares ) {
1013
1004
setHistory (history .concat ([nextSquares]))
1014
1005
setXIsNext (! xIsNext)
@@ -1018,7 +1009,7 @@ function handlePlay(nextSquares) {
1018
1009
At this point, you've moved the state to live in the ` Game ` component, and the UI should be fully
1019
1010
working, just as it was before the refactor. Here is what the code should look like at this point:
1020
1011
1021
- ``` tsx
1012
+ ``` jsx
1022
1013
import { create } from ' zustand'
1023
1014
import { combine } from ' zustand/middleware'
1024
1015
@@ -1106,7 +1097,10 @@ function Board({ xIsNext, squares, onPlay }) {
1106
1097
}
1107
1098
1108
1099
export default function Game () {
1109
- const { history, setHistory, xIsNext, setXIsNext } = useGameStore ()
1100
+ const history = useGameStore ((state ) => state .history )
1101
+ const setHistory = useGameStore ((state ) => state .setHistory )
1102
+ const xIsNext = useGameStore ((state ) => state .xIsNext )
1103
+ const setXIsNext = useGameStore ((state ) => state .setXIsNext )
1110
1104
const currentSquares = history[history .length - 1 ]
1111
1105
1112
1106
function handlePlay (nextSquares ) {
@@ -1178,9 +1172,12 @@ You'll use `map` to transform your `history` of moves into React elements repres
1178
1172
screen, and display a list of buttons to ** jump** to past moves. Let's ` map ` over the ` history ` in
1179
1173
the ` Game ` component:
1180
1174
1181
- ``` tsx {26-41 }
1175
+ ``` jsx {29-44 }
1182
1176
export default function Game () {
1183
- const { history, setHistory, xIsNext, setXIsNext } = useGameStore ()
1177
+ const history = useGameStore ((state ) => state .history )
1178
+ const setHistory = useGameStore ((state ) => state .setHistory )
1179
+ const xIsNext = useGameStore ((state ) => state .xIsNext )
1180
+ const setXIsNext = useGameStore ((state ) => state .setXIsNext )
1184
1181
const currentSquares = history[history .length - 1 ]
1185
1182
1186
1183
function handlePlay (nextSquares ) {
@@ -1230,7 +1227,7 @@ Before you can implement the `jumpTo` function, you need the `Game` component to
1230
1227
step the user is currently viewing. To do this, define a new state variable called ` currentMove ` ,
1231
1228
which will start at ` 0 ` :
1232
1229
1233
- ``` ts {3,14-21}
1230
+ ``` js {3,14-21}
1234
1231
const useGameStore = create (
1235
1232
combine (
1236
1233
{ history: [Array (9 ).fill (null )], currentMove: 0 , xIsNext: true },
@@ -1269,7 +1266,7 @@ const useGameStore = create(
1269
1266
Next, update the ` jumpTo ` function inside ` Game ` component to update that ` currentMove ` . You’ll
1270
1267
also set ` xIsNext ` to ` true ` if the number that you’re changing ` currentMove ` to is even.
1271
1268
1272
- ``` ts {2-3}
1269
+ ``` js {2-3}
1273
1270
function jumpTo (nextMove ) {
1274
1271
setCurrentMove (nextMove)
1275
1272
setXIsNext (currentMove % 2 === 0 )
@@ -1285,7 +1282,7 @@ when you click on a square.
1285
1282
` history.slice(0, currentMove + 1) ` to keep only that portion of the old history.
1286
1283
- Each time a move is made, you need to update ` currentMove ` to point to the latest history entry.
1287
1284
1288
- ``` ts {2-4}
1285
+ ``` js {2-4}
1289
1286
function handlePlay (nextSquares ) {
1290
1287
const nextHistory = history .slice (0 , currentMove + 1 ).concat ([nextSquares])
1291
1288
setHistory (nextHistory)
@@ -1297,16 +1294,14 @@ function handlePlay(nextSquares) {
1297
1294
Finally, you will modify the ` Game ` component to render the currently selected move, instead of
1298
1295
always rendering the final move:
1299
1296
1300
- ``` tsx {2-10 }
1297
+ ``` jsx {2-8 }
1301
1298
export default function Game () {
1302
- const {
1303
- history,
1304
- setHistory,
1305
- currentMove,
1306
- setCurrentMove,
1307
- xIsNext,
1308
- setXIsNext,
1309
- } = useGameStore ()
1299
+ const history = useGameStore ((state ) => state .history )
1300
+ const setHistory = useGameStore ((state ) => state .setHistory )
1301
+ const currentMove = useGameStore ((state ) => state .currentMove )
1302
+ const setCurrentMove = useGameStore ((state ) => state .setCurrentMove )
1303
+ const xIsNext = useGameStore ((state ) => state .xIsNext )
1304
+ const setXIsNext = useGameStore ((state ) => state .setXIsNext )
1310
1305
const currentSquares = history[currentMove]
1311
1306
1312
1307
function handlePlay (nextSquares ) {
@@ -1365,9 +1360,12 @@ There's no need to store `xIsNext` separately in the state. It’s better to avo
1365
1360
because it can reduce bugs and make your code easier to understand. Instead, you can calculate
1366
1361
` xIsNext ` based on ` currentMove ` :
1367
1362
1368
- ``` tsx {2,10,14 }
1363
+ ``` jsx {2-5,13,17 }
1369
1364
export default function Game () {
1370
- const { history, setHistory, currentMove, setCurrentMove } = useGameStore ()
1365
+ const history = useGameStore ((state ) => state .history )
1366
+ const setHistory = useGameStore ((state ) => state .setHistory )
1367
+ const currentMove = useGameStore ((state ) => state .currentMove )
1368
+ const setCurrentMove = useGameStore ((state ) => state .setCurrentMove )
1371
1369
const xIsNext = currentMove % 2 === 0
1372
1370
const currentSquares = history[currentMove]
1373
1371
0 commit comments