Criando tabelas dinâmicas e editáveis ​​com React

React.js permite construir formulários complexos enquanto mantém os valores dos objetos vinculados aos formulários. Um exemplo dessa complexidade é manter uma matriz de valores que podem ser modificados pelo usuário, tudo em uma única interface. Podemos expressar isso criando uma tabela onde cada linha contém um elemento de entrada correspondente ao valor de um elemento na matriz de um objeto que queremos manter.

Criar tabelas dinâmicas e editáveis ​​com React é uma tarefa comum e poderosa. Este tutorial mostrará as diferentes abordagens para construir tal formulário e como isso se relaciona com o gerenciamento de estado no React.js.

Configurar

Você criará um único componente chamado DynamicTable que mantém dois atributos de estado: um message e uma matriz de mensagens chamada items. Codifique o seguinte para você começar:

import React from 'react';

export default class DynamicTable extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      message: "",
      items: []
    }
  }

  render() {
    return (
      <div>
        <table>
          <thead>
            <tr>
              <th>Item</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
          </tbody>
        </table>
      </div>
    );
  }
}

Observe que o componente utiliza a forma completa de uma tabela HTML. React.js reclama se você não tiver o tbody ao inserir linhas em table.

Adicionando itens

Crie uma interface simples que permitirá ao usuário inserir uma mensagem e um botão para enviá-la. A ideia é que se o usuário clicar no botão, ele pegará o valor da mensagem e o adicionará ao array items. À medida que o usuário altera o valor na entrada, o estado message será atualizado. A UI ficará logo abaixo da tabela, então seu método render() será parecido com o seguinte:

render() {
  return (
    <div>
      <table>
        <thead>
          <tr>
            <th>Item</th>
            <th>Actions</th>
          </tr>
        </thead>
        <tbody>
        </tbody>
      </table>
      <hr/>
      <input type="text" />
      <button>
        Add Item
      </button>
    </div>
  );
}

Adicione o manipulador de eventos para atualizar a mensagem:

updateMessage(event) {
  this.setState({
    message: event.target.value
  });
}

Vincule o manipulador de eventos ao onChange atributo da entrada:

<input type="text" onChange={this.updateMessage.bind(this} />

A seguir, crie o manipulador de eventos para o botão quando ele for clicado:

handleClick() {
  var items = this.state.items;

  items.push(this.state.message);

  this.setState({
    items: items
  });
}

Tudo o que a função está fazendo é pegar o array atual de items e o valor atual de message e enviá-los para o array antes de atualizar o estado.

Vincule o manipulador de eventos ao onClick atributo do botão:

<button onClick={this.handleClick.bind(this)}>
  Add Item
</button>

Para renderizar items na tabela, crie uma função separada que retorne JSX renderizado:

renderRows() {
  var context = this;

  return  this.state.items.map(function(o, i) {
            return (
              <tr key={"item-" + i}>
                <td>
                  <input
                    type="text"
                    value={o}
                  />
                </td>
                <td>
                  <button>
                    Delete
                  </button>
                </td>
              </tr>
            );
          });
}

Existem dois conceitos importantes aqui:

  • Como items é uma matriz dinâmica que deve aumentar ou diminuir a qualquer momento, a função que mapeia esses valores para <tr> individuais tags devem manter um atributo key para cada nó que produz. É um requisito para React.js que o valor de seu key seja único dentro do elemento pai (neste caso, <tbody>). Assim, o valor "item-" + i é usado onde i é o índice de um elemento no array mapeado.
  • Uma variável context separada é usada para fazer referência a this já que dentro dos retornos aninhados, você precisará fazer referência à instância do < /span> posteriormente. e DynamicTable componente para vincular manipuladores de eventos a inputbutton

Invoque renderRows() dentro do corpo da tabela da seguinte maneira:

<tbody>
  {this.renderRows()}
</tbody>

Modificando Itens

Para modificar cada elemento em items dentro de input de cada linha da tabela, você terá que criar um manipulador de eventos que saiba qual índice na matriz deve ser atualizado. Crie uma função parecida com esta:

handleItemChanged(i, event) {
  var items = this.state.items;

  items[i] = event.target.value;

  this.setState({
    items: items
  });
}

Observe que o primeiro argumento da função é i, correspondendo ao índice do array. O segundo argumento é event, que possui uma propriedade target referente ao elemento input em questão. Você pode então atualizar o elemento no índice i de items atribuindo-o event.target.value.

Conecte-o ao atributo input do elemento onChange da linha da tabela:

<td>
  <input
    type="text"
    value={o}
    onChange={context.handleItemChanged.bind(context, i)}
  />
</td>

Como visto no código, context é usado para chamar handleItemChanged já que é uma referência a this, que é uma referência ao próprio componente. Em seguida, context está vinculado à função de bind() como primeiro argumento. Tudo depois de context se torna um argumento para a função. Neste caso você passa i, que é o índice fornecido pela função de mapeamento.

Excluindo itens

Você pode usar a mesma técnica para excluir itens criando um único manipulador de eventos para cada botão gerado:

handleItemDelete(i) {
  var items = this.state.items;

  items.splice(i, 1);

  this.setState({
    items: items
  });
}

O método pega o índice i e o usa como argumento para splice(index, x), que remove x itens de a matriz no índice inicial index. Neste caso, você deseja apenas remover o 1 item, que é o próprio item no índice i.

Anexe-o também ao onClick índice de vinculação de atributos i do botão:

<td>
  <button
    onClick={context.handleItemDelete.bind(context, i)}
  >
    Delete
  </button>
</td>

Código geral

O código completo é semelhante ao seguinte:

import React from 'react';

export default class DynamicTable extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      message: "",
      items: []
    }
  }

  updateMessage(event) {
    this.setState({
      message: event.target.value
    });
  }

  handleClick() {
    var items = this.state.items;

    items.push(this.state.message);

    this.setState({
      items: items,
      message: ""
    });
  }

  handleItemChanged(i, event) {
    var items = this.state.items;
    items[i]  = event.target.value;

    this.setState({
      items: items
    });
  }

  handleItemDeleted(i) {
    var items = this.state.items;

    items.splice(i, 1);

    this.setState({
      items: items
    });
  }

  renderRows() {
    var context = this;

    return  this.state.items.map(function(o, i) {
              return (
                <tr key={"item-" + i}>
                  <td>
                    <input
                      type="text"
                      value={o}
                      onChange={context.handleItemChanged.bind(context, i)}
                    />
                  </td>
                  <td>
                    <button
                      onClick={context.handleItemDeleted.bind(context, i)}
                    >
                      Delete
                    </button>
                  </td>
                </tr>
              );
            });
  }

  render() {
    return (
      <div>
        <table className="">
          <thead>
            <tr>
              <th>
                Item
              </th>
              <th>
                Actions
              </th>
            </tr>
          </thead>
          <tbody>
            {this.renderRows()}
          </tbody>
        </table>
        <hr/>
        <input
          type="text"
          value={this.state.message}
          onChange={this.updateMessage.bind(this)}
        />
        <button
          onClick={this.handleClick.bind(this)}
        >
          Add Item
        </button>
      </div>
    );
  }
}

Experimente você mesmo e veja que itens podem ser adicionados, modificados e excluídos, tudo em um único componente.

Neste guia, os elementos filhos são criados dinamicamente e dependem do valor do estado de uma matriz mantida pelo componente.

1.60 GEEK