This Gist was generated by Contrived.
Do not modify the metadata file if you want to open in Contrived again. Otherwise, it is safe to delete.
Happy Hacking!
{"user":"5f0c542a4a2ce5e528e01fdf","templateVersion":"1","templateId":"reactjs","resources":["<meta charset=\"UTF-8\" />","<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">"],"dependencies":[{"name":"react","version":"16.13.1","type":"js","url":"https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"},{"name":"react-dom","version":"16.13.1","type":"js","url":"https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"}],"files":[{"id":1,"parentId":0,"name":"public","path":"/public","type":"folder","isRoot":true,"selected":false,"expanded":false,"children":[{"id":2,"name":"index.html"}]},{"id":2,"parentId":1,"name":"index.html","path":"/src/index.html","type":"file","mimeType":"html","isRoot":false,"open":true,"selected":false,"content":""},{"id":3,"parentId":0,"name":"src","path":"/src","type":"folder","isRoot":true,"selected":false,"expanded":true,"children":[{"id":4,"name":"index.js"},{"id":5,"name":"styles.css"}]},{"id":4,"parentId":3,"name":"index.js","path":"/src/index.js","type":"file","mimeType":"es6","isRoot":false,"open":true,"selected":true,"content":""},{"id":5,"parentId":3,"name":"style.css","path":"/src/style.css","type":"file","mimeType":"css","isRoot":false,"open":true,"selected":false,"content":""}],"experimentId":"5f7b87e15556113114e18d8d"} |
body { | |
margin: 0; | |
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', | |
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', | |
sans-serif; | |
-webkit-font-smoothing: antialiased; | |
-moz-osx-font-smoothing: grayscale; | |
} | |
.square { | |
background: white; | |
border: 1px solid gray; | |
float: left; | |
margin-right: -1px; | |
margin-top: -1px; | |
padding: 0; | |
width: 80px; | |
height: 80px; | |
text-align: center; | |
font-size: 40px; | |
font-weight: bold; | |
line-Height: 80px; | |
} | |
.square:focus { | |
outline: none; | |
} | |
.boarder-row:after { | |
clear: both; | |
content: ''; | |
display: table; | |
} | |
.game { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
display: flex; | |
flex-direction: row; | |
margin: 0 auto; | |
margin-top: 150px; | |
} | |
ul { | |
padding-left: 20px; | |
} | |
.game-info { | |
margin-left: 20px; | |
} | |
.status { | |
margin-bottom: 10px; | |
} |
<div id="root"></div> |
const { Component } = React; | |
const Square = ({ value, onClick }) => { | |
return ( | |
<button className="square" onClick={onClick}> | |
{value} | |
</button> | |
) | |
} | |
const Board = ({ squares, onClick }) => { | |
const renderSquare = (i) => { | |
return ( | |
<Square value={squares[i]} onClick={() => onClick(i)} /> | |
); | |
} | |
return ( | |
<div> | |
<div className="border-row"> | |
{renderSquare(0)} | |
{renderSquare(1)} | |
{renderSquare(2)} | |
</div> | |
<div className="border-row"> | |
{renderSquare(3)} | |
{renderSquare(4)} | |
{renderSquare(5)} | |
</div> | |
<div className="border-row"> | |
{renderSquare(6)} | |
{renderSquare(7)} | |
{renderSquare(8)} | |
</div> | |
</div> | |
); | |
}; | |
class App extends Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
xIsNext: true, | |
stepNumber: 0, | |
history: [ | |
{ squares: Array(9).fill(null) } | |
] | |
}; | |
} | |
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); | |
if (winner || squares[i]) { | |
return; | |
} | |
squares[i] = this.state.xIsNext ? 'X' : 'O'; | |
this.setState({ | |
history: history.concat({ squares }), | |
xIsNext: !this.state.xIsNext, | |
stepNumber: history.length, | |
}); | |
} | |
jumpTo(step) { | |
this.setState({ | |
stepNumber: step, | |
xIsNext: (step % 2) === 0, | |
}); | |
} | |
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 : 'Start 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' : '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> | |
<ul>{moves}</ul> | |
</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[b] === squares[c]) { | |
return squares[a]; | |
} | |
} | |
return null; | |
} | |
ReactDOM.render(<App/>, document.getElementById('root')); |