Fine,again I go back in these noisy skills for prod
Today we will go around the tutorial:tic-tac-toe game
We will learn these thought:
polymorphic 多态
lift state up 状态提升
function component 函数组件
State Everywhere
class Square extends React.Component {
constructor(props){
super(props);
this.state = {
value:null
}
}
render() {
return (
<button className="square"
// set up a click event listener to go methods
onClick={() => this.props.onClick()}>
{this.props.value}
</button>
);
}
}
Lift state up
Using Array to store state
Using feature of React to bind Data and methods
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
class Board extends React.Component {
//State is stored in the Board
constructor(props){
super(props)
this.state = {
squares:Array(9).fill(null),
xIsNext:true
}
}
//方法
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = this.state.xIsNext ? 'X' : 'O';//这句话牛逼
this.setState({
squares: squares,
xIsNext: !this.state.xIsNext//这句话牛逼
});
}
//给Square绑定
renderSquare(i) {
//Since the Board passed onClick={() => this.handleClick(i)}
//to Square
//the Square calls this.handleClick(i) when clicked.
return <Square value={this.state.squares[i]}
onClick={() => this.handleClick(i)} />;
}
render() {
//let status = 'Next player: X';
//还能这样?
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>
);
}
}
- The onClick prop on the built-in DOM <button> component tells React to set up a click event listener.
- When the button is clicked, React will call the onClick event handler that is defined in Square’s render() method.
This event handler calls - this.props.onClick(). The Square’s onClick prop was specified by the Board.
- Since the Board passed onClick={() => this.handleClick(i)} to Square, the Square calls this.handleClick(i) when clicked.
Lift state up twice
Why?Because we want to have the whole situation state could be stored or even dated back, which is the reason why we use .slice() instead of modifying the existing array
Immutability Is Important
Have board be a num render and a middleman for Square
1:Move off the constructor(state) to Game component using correct data construction
2:Move to method of controling state to Game component correctly(sometimes needs to change)
3:Don't forget to connect Game and board by:return ( //.... <Board squares={current.squares} onClick={(i) => >this.handleClick(i)} /> </div> )
and be careful about all the minor details
<div>{ status }</div>//in return of Game
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
//have changed in return of Board from this.state.squares[i] and this.handleClick(i)
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 = {
history: [{
squares: Array(9).fill(null),
}],
xIsNext: true
};
}
handleClick(i) {
const history = this.state.history;
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,
}]),
xIsNext: !this.state.xIsNext,
});
}
render() {
const history = this.state.history;
const current = history[history.length - 1];
const winner = calculateWinner(current.squares);
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>{/* TODO */}</ol>
</div>
</div>
);
}
}
Now we could have many state situations in the Object Array and never lose them.