I am new to react.js and this is literaly the code provided in the tutorial on their website to make a tic tac toe game. However, when i try and compile, i get -> 'React' must be in scope when using JSX. I have copied and pasted tutorial code. I am currently using vim editor. If there is a better editor to use please suggest!
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
class Board extends React.Component {
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)} //onclick button
/>
);
}
render() {
return (
<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 {
constructor(props) {
super(props);
this.state = {
history: [
{
squares: Array(9).fill(null)
}
],
stepNumber: 0,
xIsNext: true
};
}
handleClick(i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? "X" : "O";
this.setState({
history: history.concat([
{
squares: squares
}
]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext
});
}
jumpTo(step) {
this.setState({
stepNumber: step,
xIsNext: (step % 2) === 0
});
}
render() {
const history = this.state.history; //to store history of
//game
const current = history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li key={move}>
<button onClick={() => this.jumpTo(move)}>{desc}</button>
</li>
);
});
let status;
if (winner) {
status = "Winner: " + winner;
} else {
status = "Next player: " + (this.state.xIsNext ? "X" : "O");
}
return (
<div className="game">
<div className="game-board">
<Board
squares={current.squares}
onClick={i => this.handleClick(i)}
/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
);
}
}
// ========================================
ReactDOM.render(<Game />, document.getElementById("root"));
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] ===
squares[c]) {
return squares[a];
}
}
return null;
}
Since you are using class Game extends React.Component you will need the dependency to the included in the file on the top. Also if ReactDOM.render Call is in the same file, you will have to include that dependency too.
Also I hope you have React and ReactDOM dependencies in package.json file.
Put this at the top of the file.
Import React from ‘react’;
Import ReactDOM from ‘react-dom’;
Related
I am going through the Reactjs doc and I tried adding a little feature to the tic-tac-toe game in the tutorial of the documentation. In the helper function that tells if a player won, there was no way to tell if the game ended as a draw. so I added this feature, however, there is a bug in my code, so that when the 9 squares are complete, and there is obviously a winner, sometimes it shows that there is a winner and sometimes it shows a draw. Please help to figure out this bug. Thank you.
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
xIsNext: true,
}
}
handleClick(i){
//console.log(this.state.counter)
const squares = this.state.squares.slice();
if(calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? 'X' : 'O';
this.setState({squares: squares, xIsNext: !this.state.xIsNext,})
}
renderSquare(i) {
return <Square
value={this.state.squares[i]}
onClick={()=>this.handleClick(i)}
/>;
}
render() {
const winner = calculateWinner(this.state.squares);
let status;
if(winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
}
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>
);
}
}
// ========================================
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Game />);
function calculateWinner(squares, counter) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
const squaresFilled = squares.every(value => value !== null);
const threeSquaresEqual = squares[a] && squares[a] === squares[b] && squares[a] === squares[c]
console.log(!threeSquaresEqual)
if(threeSquaresEqual) {
return squares[a];
} else if(squaresFilled && threeSquaresEqual) {
return "Draw";
}
}
return null;
}
Instead of adding the logic at the winner just use a new state in the constructor to count the turns and evaluate if board has been completed without a winner
Add a new state to check on turns count
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
xIsNext: true,
turns: 0
}
}
Make the counter go up on the click event
handleClick(i){
//console.log(this.state.counter)
const squares = this.state.squares.slice();
if(calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? 'X' : 'O';
this.setState({
squares: squares,
xIsNext: !this.state.xIsNext,
turns: this.state.turns + 1
})
}
Then when rendering, if turns are 9 and winner is null then render "Draw"
render() {
const winner = calculateWinner(this.state.squares);
let status;
if(winner == null && turns == 9) {
status = 'Draw';
} else if(winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
}
return (<> ... </>)
}
Something like that.
Can someone briefly explain why this keyword is undefined in a function expression, but defined in an arrow function. I'm simply trying to follow the tutorial on the React website and they gloss over a seemingly important concept regarding confusing behavior of this but the link refers to a very in-depth article which is not at all friendly to beginners. After completing the tutorial I wanted to play around and try to understand what works and what doesn't and I tried replacing an arrow function with a function expression, and suddenly the onClick event failed to work.
This is the code as written in the tutorial:
class Board extends React.Component {
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
onClick={() => {
this.props.onClick(i);
}} />
);
}
//other stuff
}
And I wanted to change the arrow function to a traditional function expression, but had some problems
class Board extends React.Component {
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
onClick={function() {
console.log(this); //undefined
console.log(this.props); //throws exception
this.props.onClick(i); //throws exception
}} />
);
}
}
Why is this undefined in the function expression? When I read the documentation for arrow function and function expressions on MDN it seems that the opposite should be true. What's going on here?
Here the codepen
Here's the full project:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
class Board extends React.Component {
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i) } />
);
}
render() {
return (
<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 {
constructor(props) {
super(props);
this.state = {
isX: true,
history: [{
squares: Array(9).fill(null)
}],
stepNumber: 0
}
}
handleClick(i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = this.state.history[history.length - 1];
const squares = current.squares.slice();
if (squares[i] == null) {
squares[i] = this.state.isX ? 'X' : 'O';
} else {
console.log("Square [" + i + "] is already marked!");
return;
}
this.setState({
history: history.concat([{squares: squares}]),
isX: !this.state.isX,
stepNumber: history.length
});
}
jumpTo(step) {
this.setState({
stepNumber: step,
xIsNext: (step % 2) === 0,
});
}
render() {
const history = this.state.history;
const current = this.state.history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li key={move}>
<button onClick={() => this.jumpTo(move)}>{desc}</button>
</li>
);
});
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (this.state.isX ? 'X' : 'O');
}
return (
<div className="game">
<div className="game-board">
<Board
squares={current.squares}
onClick={(i) => this.handleClick(i)}/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
);
}
}
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
// ========================================
ReactDOM.render(
<Game />,
document.getElementById('root')
);
I am trying to create a simple tic tac toe gaming using functional components. However, I seem to have an issue where my state is always initially one step behind on the first click of a square. I know this question has been asked many times and I have read through a lot of posts, but I am still struggling to get my state in sync. I also noticed that when I call my board reset function instead of starting with X as it should it tries to start with O which technically should not be possible since it is being set back to its initial state. Any help or suggestions anyone has would be greatly appreciated!
App.js
import React, { useState, useEffect } from 'react';
import Board from './Board/Board';
import { calculateWinner } from '../shared/utils';
import { DEFAULT_BOARD } from '../shared/constants';
import './App.css';
const App = () => {
const [state, setState] = useState({
squares: Array(9).fill(null),
stepNumber: 0,
xIsNext: true,
status: "Next player: X",
moves: 0
});
useEffect(() => {
const winner = calculateWinner(state.squares);
if(winner) {
setState((prevState) => ({
...prevState,
status: 'Winner: ' + winner
}));
}
if(state.moves >= 9) {
setState((prevState) => ({
...prevState,
status: 'Its a draw!'
}));
}
}, [state.squares, state.moves]);
const resetBoard = () => {
setState({...DEFAULT_BOARD});
}
const handleClick = (i) => {
const tempSquares = state.squares.slice();
if (calculateWinner(tempSquares) || tempSquares[i]) {
return;
}
let temp = !state.xIsNext
tempSquares[i] = state.xIsNext ? 'X' : 'O';
setState((prevState) => ({
...prevState,
xIsNext: temp,
moves: state.moves + 1,
squares: tempSquares,
status: 'Next player: ' + (state.xIsNext ? "X" : "O")
}))
}
return (
<div className="game">
<Board handleClick={handleClick} status={state.status} squares={state.squares} resetBoard={resetBoard}/>
</div>
);
}
export default App;
Board.js
import Square from '../Square/Square';
import './Board.css';
const Board = ({status, handleClick, squares, resetBoard}) => {
const renderSquare = (i) => {
return (
<Square value={squares[i]} onClick={handleClick} index={i} />
);
}
return (
<Fragment>
<div className="board">
<div className="status">{status}</div>
<div className="board-grid">
{squares.map((value, index) => {
return (
<div key={index}>
{renderSquare(index)}
</div>
)
})}
</div>
<button onClick={resetBoard}>Reset Board</button>
</div>
</Fragment>
)
}
export default Board;
Square.js
import React from 'react';
import './Square.css'
const Square = ({onClick, value, index}) => {
return (
<button className="square" onClick={() => onClick(index)}>
{value}
</button>
)
}
export default Square;
util.js
export const calculateWinner = (squares) => {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
constants.js
export const DEFAULT_BOARD = {
squares: Array(9).fill(null),
xIsXNext: true,
status: "Next player: X",
moves: 0
}
First let me say, thank you for any help/advice I may receive. I am truly grateful.
I've built a tic-tac-toe app in react (mostly from youtube). It currently only works as a local multiplayer game. I would like to add an option for the player to choose between local multiplayer, or against AI. The ai doesn't need to be advanced, simply picking a random available space will do fine.
My Game.js file looks like this:
import React, { Component } from 'react'
import Board from './Board';
export default class Game extends Component {
constructor(props){
super(props);
this.state= {
xIsNext: true,
stepNumber: 0,
history: [
{ squares: Array(9).fill(null) }
]
}
}
jumpTo(step){
this.setState({
stepNumber: step,
xIsNext: (step%2)===0
})
}
handleClick(i) {
const history = this.state.history.slice(0,this.state.stepNumber+1);
const current = history[history.length-1];
const squares = current.squares.slice();
const winner = calculateWinner(squares);
//stops player from picking a chosen square
if(winner || squares[i]){
return;
}
squares[i] = this.state.xIsNext?'X':'0';
this.setState({
history: history.concat({
squares: squares
}),
xIsNext: !this.state.xIsNext,
stepNumber: history.length
});
}
render() {
const history = this.state.history;
const current = history[this.state.stepNumber];
const winner= calculateWinner(current.squares);
const moves= history.map((step, move)=>{
const desc = move? 'Go to #' + move:'Choose a square to begin the game';
return (
<li key={move}>
<button onClick={()=>{this.jumpTo(move)}}>
{desc}
</button>
</li>
)
});
let status;
if(winner){
status = 'Winner is ' + winner
} else{
status = 'Next Player is ' + (this.state.xIsNext?'X':'0');
}
return (
<div className="game">
<div className="game-board">
<Board onClick={(i)=>this.handleClick(i)}
squares={current.squares} />
</div>
<div>{status}</div>
<ul>{moves}</ul>
</div>
)
}
}
//sets win condition
function calculateWinner(squares){
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
for(let i=0;i<lines.length;i++){
const [a,b,c] = lines[i];
if(squares[a] && squares[a] === squares[b] && squares[b] === squares[c]){
return squares [a];
}
}
return null;
}
Add an optional param to handleClick(), let's call it computerIsGoing. At the end of handleClick(), add this code:
if ( ! computerIsGoing ) {
this.computersTurn()
}
The computersTurn() function would look something like this (pseudocode):
function computerIsGoing() {
// Look at all the squares, find all the ones not yet filled in, pick a random one (e.g. 5)
let n = 5; // Assume
this.handleClick( n, true );
}
This will cause the computersTurn() function to run after every time the user clicks a square.
My task is to build a tic tac toe game using react. One of the things that I need to implement is the ability to undo previous moves. I'm looking for some help with removing a single element from an array based on the selection. I have an if/else if statement that checks if the selected box has a value of X or O. If it does I need to delete that value from the board.
class GameBoard extends Component {
constructor(props) {
super(props);
this.state = {
box: Array(9).fill(''),
isNext: true
};
}
handleClick(i) {
debugger
const box = this.state.box.slice();
if (box[i].includes('X') || box[i].includes('O')) {
} else if (box[i].includes('')) {
box[i] = this.state.isNext ? 'X' : 'O';
this.setState({ box: box, isNext: !this.state.isNext });
}
}
renderSquare(i) {
return <Selection value={this.state.box[i]} onClick={() => this.handleClick(i)} />
}
render() {
const winner = calculateWinner(this.state.box);
let status;
if (winner) {
status = 'Winner: ' + winner;
} else if (winner && winner === 'Draw') {
status = winner;
}
else {
status = 'Next Player: ' + (this.state.isNext ? 'X' : 'O');
}
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>
);
}
}
function calculateWinner(box) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (box[a] && box[a] === box[b] && box[a] === box[c]) {
return box[a];
}
else if (!box.includes('')) {
return 'Draw';
}
}
return null;
}
export default GameBoard;
You could use the index i to update the corresponding item value in array of boxes to achieve this:
handleClick(i) {
debugger
const box = this.state.box.slice();
if (box[i].includes('X') || box[i].includes('O')) {
box[i] = '' // Reset the value of box item at i in box array
this.setState({ box: box, isNext: !this.state.isNext }); // Trigger re-render
} else if (box[i].includes('')) {
box[i] = this.state.isNext ? 'X' : 'O';
this.setState({ box: box, isNext: !this.state.isNext });
}
}