Aula 03 - React - Imutabilidade

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no Facebook

Esse é o link do código fluente no Pinterest

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Melhore seu NETWORKING

Participe de comunidades de desenvolvedores:

Fiquem a vontade para me adicionar ao linkedin.

E também para me seguir no GITHUB.

Ah, se puder, clica na estrela nos meus repositórios pra dá uma força ao meu perfil no GITHUB

Código da branch inicial:

https://github.com/toticavalcanti/tutorial-react/tree/tic_tac_toe

Link da documentação oficial:

https://reactjs.org/tutorial/

Por que imutabilidade é Importante

No código do exemplo usamos o método slice() para criar uma cópia do array de quadrados ao invés de modificar o existente. Iremos agora discutir imutabilidade e porque ela é importante de se aprender. Geralmente existem duas maneiras de se alterar dados. A primeira é mudar (mutar) o dado alterando diretamente seu valor. A segunda maneira é substituir o dado antigo por uma nova cópia com as alterações desejadas.

Mudando dados com mutação


let player = {score: 1, name: 'Jeff'};
player.score = 2;
// Agora o player é {score: 2, name: 'Jeff'}

Mudando dados sem mutação


let player = {score: 1, name: 'Jeff'};

let newPlayer = Object.assign({}, player, {score: 2});
// Agora o player não sofreu alteração, mas o newPlayer é {score: 2, name: 'Jeff'}

// Ou então se você estiver usando a sintaxe "object spread", você pode escrever:
// let newPlayer = {...player, score: 2};
O resultado final é o mesmo, mas, por não mudar (ou alterar os dados) diretamente, ganhamos vários benefícios.

Complexidade das features se tornam mais simples

Imutabilidades faz a complexidade das features se tornarem bem mais simples de serem implementadas. Mais a frente, implementaremos uma feature de "máquina do tempo" que nos permitirá revisar o histórico do jogo da velha e "voltar" as jogadas anteriores. Essa funcionalidade não está ligada somente ao jogo, uma habilidade de desfazer e refazer certas ações é um requisito comum em aplicações. Evitar mutação nos permite manter o histórico das versões anteriores do jogo intacta e reutiliza-las mais tarde.

Detectar Mudanças

Detectar mudanças e objetos que foram mudados é difícil, pois, eles são modificados diretamente. Essa detecção requer um objeto que foi alterado para ser comparado com as cópias das suas próprias versões anteriores e a árvore inteira do object para ser cruzada. Detectar mudanças em objetos imutáveis é consideravelmente fácil. Se ele for imutável e o que está sendo referenciado for diferente do anterior, concluímos que o objeto foi alterado.

Determinar Quando Re-renderizar no React

O principal benefício da imutabilidade é que ela ajuda a construir componentes puros em React. Dados imutáveis podem facilmente determinar se foram feitas mudanças, que ajudarão a decidir quando um componente precisa ser renderizado novamente.

Componentes de Função

Nós vamos agora mudar o Square para ser um componente de função. Em React, componentes de função são os mais simples de serem escritos, contém apenas um método render e não possuem seu próprio state. Ao invés de definir uma classe que extende de React.Component, nós podemos escrever uma função que recebe props como entrada e retorna o que deverá ser renderizado. Esse tipo de componente é menos tedioso de escrever do que classes e muitos componentes podem ser expressados desta maneira. Troque a classe Square por esta função: src/index.js

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),
  };
  handleClick(i) { 
    const squares = this.state.squares.slice(); 
    squares[i] = 'X'; this.setState({squares: squares}); }
  renderSquare(i) {    
    return <Square value={this.state.squares[i]} />;
  }
  renderSquare(i) {
    return (
      <Square
        value={this.state.squares[i]}
        onClick={() => this.handleClick(i)}
      />
    );
  }
  render() 
    const status = 'Next player: X';
    return (
      React.createElement("div", null,
      React.createElement("div", { className: "status" }, status),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(0),
      this.renderSquare(1),
      this.renderSquare(2)),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(3),
      this.renderSquare(4),
      this.renderSquare(5)),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(6),
      this.renderSquare(7),
      this.renderSquare(8))));
  }
}
class Game extends React.Component {
  render() {
    return (
      React.createElement("div", { className: "game" },
      React.createElement("div", { className: "game-board" },
      React.createElement(Board, null)),
      React.createElement("div", { className: "game-info" },
      React.createElement("div", null),
      React.createElement("ol", null))));
  }
}
// ========================================
ReactDOM.render(
React.createElement(Game, null),
document.getElementById('root'));
Nos modificamos this.props para props nas duas vezes em que ele aparece.

Nota

Quando modificamos Square para ser um componente funcional, também modificamos onClick={() => this.props.onClick()} para uma versão mais curta: onClick={props.onClick} Note a ausência dos parentêses em ambos os lados.

Alternando os jogadores

Agora precisamos consertar um defeito óbvio em nosso Jogo da Velha: Os "O"s não podem ser marcados no tabuleiro. Vamos definir a primeira jogada para ser "X" por padrão. Podemos definir esse padrão modificando o state inicial no construtor do nosso tabuleiro (Board). src/index.js

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) { 
    const squares = this.state.squares.slice(); 
    squares[i] = 'X'; this.setState({squares: squares}); }
    renderSquare(i) {    
      return <Square value={this.state.squares[i]} />;
  }
  renderSquare(i) {
    return (
      <Square
        value={this.state.squares[i]}
        onClick={() => this.handleClick(i)}
      />
    );
  }
  render() 
    const status = 'Next player: X';
    return (
      React.createElement("div", null,
      React.createElement("div", { className: "status" }, status),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(0),
      this.renderSquare(1),
      this.renderSquare(2)),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(3),
      this.renderSquare(4),
      this.renderSquare(5)),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(6),
      this.renderSquare(7),
      this.renderSquare(8))));
  }
}
class Game extends React.Component {
  render() {
    return (
      React.createElement("div", { className: "game" },
      React.createElement("div", { className: "game-board" },
      React.createElement(Board, null)),
      React.createElement("div", { className: "game-info" },
      React.createElement("div", null),
      React.createElement("ol", null))));
  }
}
// ========================================
ReactDOM.render(
React.createElement(Game, null),
document.getElementById('root'));
Sempre que um jogador fizer uma jogada, xIsNext (um boolean) será trocado para determinar qual jogador será o próximo e o state do jogo será salvo. Nós atualizaremos a função handleClick() do Board para trocar o valor de xIsNext: src/index.js

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) {
    const squares = this.state.squares.slice();
    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 status = 'Next player: X';
    return (
      React.createElement("div", null,
      React.createElement("div", { className: "status" }, status),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(0),
      this.renderSquare(1),
      this.renderSquare(2)),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(3),
      this.renderSquare(4),
      this.renderSquare(5)),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(6),
      this.renderSquare(7),
      this.renderSquare(8))));
  }
}
class Game extends React.Component {
  render() {
    return (
      React.createElement("div", { className: "game" },
      React.createElement("div", { className: "game-board" },
      React.createElement(Board, null)),
      React.createElement("div", { className: "game-info" },
      React.createElement("div", null),
      React.createElement("ol", null))));
  }
}
// ========================================
ReactDOM.render(
React.createElement(Game, null),
document.getElementById('root'));
Com essa mudança,"X"s e "O"s serão alternados. Tente! Também vamos modificar o texto de "status" na função render() do Board, para que ela passe a exibir quem jogará no próxima jogada. src/index.js

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) {
    const squares = this.state.squares.slice();
    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 status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
    return (
      React.createElement("div", null,
      React.createElement("div", { className: "status" }, status),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(0),
      this.renderSquare(1),
      this.renderSquare(2)),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(3),
      this.renderSquare(4),
      this.renderSquare(5)),
      React.createElement("div", { className: "board-row" },
      this.renderSquare(6),
      this.renderSquare(7),
      this.renderSquare(8))));
  }
}
class Game extends React.Component {
  render() {
    return (
      React.createElement("div", { className: "game" },
      React.createElement("div", { className: "game-board" },
      React.createElement(Board, null)),
      React.createElement("div", { className: "game-info" },
      React.createElement("div", null),
      React.createElement("ol", null))));
  }
}
// ========================================
ReactDOM.render(
React.createElement(Game, null),
document.getElementById('root'));

Por essa aula é só! ;)

Código final da aula:

https://github.com/toticavalcanti/tutorial-react/tree/tic_tac_toe

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no Facebook

Link do código fluente no Pinterest

Novamente deixo meus link de afiliados:

Hostinger

Digital Ocean

One.com

Obrigado, até a próxima e bons estudos. ;)