0

I have the following data:

var data = [
        ["The Lord of the Rings", "J. R. R. Tolkien", "English", "1954-1955", "150 million"], 
        ["Le Petit Prince (The Little Prince)", "Antoine de Saint-Exupéry", "French", "1943", "140 million"], 
        ["Harry Potter and the Philosopher's Stone", "J. K. Rowling", "English", "1997", "107 million"], 
        ["And Then There Were None", "Agatha Christie", "English", "1939", "100 million"], 
        ["Dream of the Red Chamber", "Cao Xueqin", "Chinese", "1754-1791", "100 million"], 
        ["The Hobbit", "J. R. R. Tolkien", "English", "1937", "100 million"], 
        ["She: A History of Adventure", "H. Rider Haggard", "English", "1887", "100 million"],
      ];

and I use these to render a table with a row for each item (books with their data). For learning purposes I want to add a new item to the array and I do it with this code:

  var valueToPush = new Array();
  valueToPush[0]="Frodi societarie";
  valueToPush[1]="Lelio Faieta";
  valueToPush[2]="Italiano";
  valueToPush[3]="2004";
  valueToPush[4]="10";

  var myTimer = setTimeout(function() {data.push(valueToPush); console.log(data);}, 5000);

My goal is then to have the new line added into the table. The table is rendered with this code:

  var Ex = ReactDOM.render(
    React.createElement(Excel, {
      headers: headers,
      initialData: data,
    }),
    document.getElementById("app")
  );

What is the next step to get the table re-rendered? The following is my full code so far:

  var Excel = React.createClass({
    displayName: 'Excel',

    propTypes: {
      headers: React.PropTypes.arrayOf(
        React.PropTypes.string
      ),
      initialData: React.PropTypes.arrayOf(
        React.PropTypes.arrayOf(
          React.PropTypes.string
        )
      ),
    },

    getInitialState: function() {
      return {
        data: this.props.initialData,
        sortby: null,
        descending: false,
        edit: null, // [row index, cell index],
        search: false,
      };
    },

    _sort: function(e) {
      var column = e.target.cellIndex;
      var data = this.state.data.slice();
      var descending = this.state.sortby === column && !this.state.descending;
      data.sort(function(a, b) {
        return descending 
          ? (a[column] < b[column] ? 1 : -1)
          : (a[column] > b[column] ? 1 : -1);
      });
      this.setState({
        data: data,
        sortby: column,
        descending: descending,
      });
    },

    _showEditor: function(e) {
      this.setState({edit: {
        row: parseInt(e.target.dataset.row, 10),
        cell: e.target.cellIndex,
      }});
    },

    _save: function(e) {
      e.preventDefault();
      var input = e.target.firstChild;
      var data = this.state.data.slice();
      data[this.state.edit.row][this.state.edit.cell] = input.value;
      this.setState({
        edit: null,
        data: data,
      });
    },

    _preSearchData: null,

    _toggleSearch: function() {
      if (this.state.search) {
        this.setState({
          data: this._preSearchData,
          search: false,
        });
        this._preSearchData = null;
      } else {
        this._preSearchData = this.state.data;
        this.setState({
          search: true,
        });
      }
    },

    _search: function(e) {
      var needle = e.target.value.toLowerCase();
      if (!needle) {
        this.setState({data: this._preSearchData});
        return;
      }
      var idx = e.target.dataset.idx;
      var searchdata = this._preSearchData.filter(function(row) {
        return row[idx].toString().toLowerCase().indexOf(needle) > -1;
      });
      this.setState({data: searchdata});
    },

    _download: function(format, ev) {
      var contents = format === 'json'
        ? JSON.stringify(this.state.data)
        : this.state.data.reduce(function(result, row) {
            return result
              + row.reduce(function(rowresult, cell, idx) {
                  return rowresult 
                    + '"' 
                    + cell.replace(/"/g, '""')
                    + '"'
                    + (idx < row.length - 1 ? ',' : '');
                }, '')
              + "\n";
          }, '');

      var URL = window.URL || window.webkitURL;
      var blob = new Blob([contents], {type: 'text/' + format});
      ev.target.href = URL.createObjectURL(blob);
      ev.target.download = 'data.' + format;
    },

    render: function() {
      return (
        React.DOM.div(null,
          this._renderToolbar(),
          this._renderTable()
        )
      );
    },

    _renderToolbar: function() {
      return  React.DOM.div({className: 'toolbar'},
        React.DOM.button({
          onClick: this._toggleSearch,
        }, 'Search'),
        React.DOM.a({
          onClick: this._download.bind(this, 'json'),
          href: 'data.json',
        }, 'Export JSON'),
        React.DOM.a({
          onClick: this._download.bind(this, 'csv'),
          href: 'data.csv',
        }, 'Export CSV')
      );
    },

    _renderSearch: function() {
      if (!this.state.search) {
        return null;
      }
      return (
        React.DOM.tr({onChange: this._search},
          this.props.headers.map(function(_ignore, idx) {
            return React.DOM.td({key: idx},
              React.DOM.input({
                type: 'text',
                'data-idx': idx,
              })
            );
          })
        )
      );
    },

    _renderTable: function() {
      return (
        React.DOM.table(null,
          React.DOM.thead({onClick: this._sort},
            React.DOM.tr(null,
              this.props.headers.map(function(title, idx) {
                if (this.state.sortby === idx) {
                  title += this.state.descending ? ' \u2191' : ' \u2193';
                }
                return React.DOM.th({key: idx}, title);
              }, this)
            )
          ),
          React.DOM.tbody({onDoubleClick: this._showEditor},
            this._renderSearch(),
            this.state.data.map(function(row, rowidx) {
              return (
                React.DOM.tr({key: rowidx},
                  row.map(function(cell, idx) {
                    var content = cell;
                    var edit = this.state.edit;
                    if (edit && edit.row === rowidx && edit.cell === idx) {
                      content = React.DOM.form({onSubmit: this._save},
                        React.DOM.input({
                          type: 'text',
                          defaultValue: cell,
                        })
                      );
                    }

                    return React.DOM.td({
                      key: idx,
                      'data-row': rowidx,
                    }, content);
                  }, this)
                )
              );
            }, this)
          )
        )
      );
    }
  });

  var headers = [
    "Book", "Author", "Language", "Published", "Sales"
  ];

  var data = [
    ["The Lord of the Rings", "J. R. R. Tolkien", "English", "1954-1955", "150 million"], 
    ["Le Petit Prince (The Little Prince)", "Antoine de Saint-Exupéry", "French", "1943", "140 million"], 
    ["Harry Potter and the Philosopher's Stone", "J. K. Rowling", "English", "1997", "107 million"], 
    ["And Then There Were None", "Agatha Christie", "English", "1939", "100 million"], 
    ["Dream of the Red Chamber", "Cao Xueqin", "Chinese", "1754-1791", "100 million"], 
    ["The Hobbit", "J. R. R. Tolkien", "English", "1937", "100 million"], 
    ["She: A History of Adventure", "H. Rider Haggard", "English", "1887", "100 million"],
  ];
  this.state(data);

  var myTimer = setTimeout(function() {this.setState([this.state,["Frodi","Lelio","Italiano","2004","10"]]); console.log(data);}, 5000);
  var Ex = ReactDOM.render(
    React.createElement(Excel, {
      headers: headers,
      initialData: data,
    }),
    document.getElementById("app")
  );

3 Answers 3

2

you have to put your data in a state, then whenever you update the state a rerender will happen

const [data, setData] = useState([
        ["The Lord of the Rings", "J. R. R. Tolkien", "English", "1954-1955", "150 million"], 
        ["Le Petit Prince (The Little Prince)", "Antoine de Saint-Exupéry", "French", "1943", "140 million"], 
        ["Harry Potter and the Philosopher's Stone", "J. K. Rowling", "English", "1997", "107 million"], 
        ["And Then There Were None", "Agatha Christie", "English", "1939", "100 million"], 
        ["Dream of the Red Chamber", "Cao Xueqin", "Chinese", "1754-1791", "100 million"], 
        ["The Hobbit", "J. R. R. Tolkien", "English", "1937", "100 million"], 
        ["She: A History of Adventure", "H. Rider Haggard", "English", "1887", "100 million"],
      ]);

setData([..data,  ["Frodi societarie", "Lelio Faieta", "Italiano", "2004", "10"]);

UPDATE

As you are using react 15 you cannot use hooks:

import React from "react";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      books: [
        [
          "The Lord of the Rings",
          "J. R. R. Tolkien",
          "English",
          "1954-1955",
          "150 million"
        ],
        [
          "Le Petit Prince (The Little Prince)",
          "Antoine de Saint-Exupéry",
          "French",
          "1943",
          "140 million"
        ],
        [
          "Harry Potter and the Philosopher's Stone",
          "J. K. Rowling",
          "English",
          "1997",
          "107 million"
        ],
        [
          "And Then There Were None",
          "Agatha Christie",
          "English",
          "1939",
          "100 million"
        ],
        [
          "Dream of the Red Chamber",
          "Cao Xueqin",
          "Chinese",
          "1754-1791",
          "100 million"
        ],
        ["The Hobbit", "J. R. R. Tolkien", "English", "1937", "100 million"],
        [
          "She: A History of Adventure",
          "H. Rider Haggard",
          "English",
          "1887",
          "100 million"
        ]
      ]
    };
  }

  componentDidMount() {
    setTimeout(() => {
      this.setState({
        books: [
          ...this.state.books,
          ["Frodi societarie", "Lelio Faieta", "Italiano", "2004", "10"]
        ]
      });
    }, 5000);
  }

  render() {
    return (
      <ul>
        {this.state.books.map((book, index) => (
          <li key={index}>{book[0]}</li>
        ))}
      </ul>
    );
  }
}

export default App;

Here the codesandbox: https://codesandbox.io/s/priceless-leftpad-3338g

Sign up to request clarification or add additional context in comments.

8 Comments

so you'd put the setData call in the setTimeout function to trigger display?
doesn't matter where you call setData, it will trigger a re-render, even inside a setTimeout
I get that useState is not defined. Am I missing something? I am working with react 15, probably that's the reason? If I read it correctly this is something for 16+ (still under dev)
you have to import it from the react package: import { useState} from 'react';
if you want put your code in codesandbox.io i'll help you to debug
|
1

Please go through react official documentation, It has everything in it. BTW

this.setState({
    tableData: [
        ...data,
        valueToPush 
    ] 
})

Comments

-1

You should always use setState to trigger re-render in case of class based components and method from useState in case of functional component.

class App extends React.Component {
 constructor(props) {
super(props)
this.state = {
    name: 'my name'
}
}
clickHandler() {
// 1st case
  this.setState({name: 'new name'})

// 2nd case
//this.state.name = 'new name'
}
render() {
return (
  <div>
   {this.state.name}
         <button onClick={this.clickHandler.bind(this)} > change 
  Name </button>
  </div>
)
}
}

ReactDOM.render(<App />, document.querySelector("#app"))

Toggle between 1st and 2nd case to see how this works

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.