2

I am learning ReactJS.

I have pre-existing table rendered which contains thead and only 1 row by default. Now on button click, I want to append a row everytime the button click, but the max rows appended should not be greater than 4.

Here is my code:

import React, { Component } from "react";
import Sidebar from "../Home/Sidebar";
import axios from "axios";
import $ from "jquery";
import { isDivisibleBy100 } from "../utils/utility";
import { Chart } from "react-charts";

class Strategy extends Component {
  state = {
    Price: [],
    chart_data: [],
    loadData: true,
    unit: parseFloat(0),
    loadUnit: true,

  };

  componentDidMount() {
    this.getPriceList();
  }

  getPriceList() {
    axios.get("http://localhost:8000/listprice/").then(res => {
      if (res.data.result === 1) {
        this.setState({ Price: res.data.data });
      }
    });
  }


  appendRow(event) {
    var rel = event.target.getAttribute("rel");
    rel = parseInt(rel) + 1;
    console.log(rel);
    var addRow = (
      <tr>
        <td>
          <input type="text" id={`select-type` + rel} />
        </td>
        <td>
          <input type="text" id={`select-position` + rel} />
        </td>
      </tr>
    );
    $(".table tbody").append(appRow);
  }

  render() {
    return (
      <div className="container container_padding">
        <div className="row">
          <Sidebar />
          <div className="col-md-9 col-sm-9 col-xs-12 white-box">
            <div className="col-sm-12">
              <h3 className="col-sm-4" style={{ padding: "0px" }}>
                Strategy Plan:
              </h3>
              <div className="col-sm-7" />
              <div className="col-sm-1" style={{ marginTop: "15px" }}>
                <button
                  rel="1"
                  type="button"
                  id="addbtn"
                  className="btn btn-circle"
                  onClick={this.appendRow}
                >
                  <i className="fa fa-plus" />
                </button>
              </div>
            </div>
            <div className="col-sm-12 a">
              <div className="table-responsive">
                <table className="table table-bordered">
                  <thead>
                    <tr>
                      <td>#</td>
                      <td>Type</td>
                      <td>Position</td>
                      <td>Price</td>
                      <td>Number</td>
                    </tr>
                  </thead>
                  <tbody>
                    <tr>
                      <td>1</td>
                      <td>
                        <select
                          className="form-control"
                          name="select-type"
                          id="select-type"
                        >
                          <option value="select">Select</option>
                          <option value="one">1</option>
                          <option value="two">2</option>
                        </select>
                      </td>
                      <td>
                        <select
                          className="form-control"
                          name="select-position"
                          id="select-position"
                        >
                          <option value="select">Select</option>
                          <option value="a">A</option>
                          <option value="b">B</option>
                        </select>
                      </td>
                      <td>
                        <select
                          className="form-control"
                          name="price-list"
                          id="price-list"
                          onChange={event =>
                            this.handlePriceChange(event)
                          }
                        >
                          <option value="select">Select</option>
                          {this.state.Price.map(p => (
                            <option
                              value={p.pprice}
                              key={p.price}
                            >
                              {p.price}
                            </option>
                          ))}
                        </select>
                      </td>
                      <td style={{ width: "180px" }}>
                        <input
                          id="input-number"
                          type="text"
                          className="form-control"
                          defaultValue="1"
                        />
                      </td>
                    </tr>


                  </tbody>
                </table>
              </div>
            </div>
            <div className="col-sm-12">
              <button
                className="btn"
                onClick={() => this.handleClick()}
              >
                Calculate
              </button>
            </div>

            {this.state.loadData ? (
              ""
            ) : (
              <div
                style={{
                  width: "600px",
                  height: "300px",
                  marginTop: "35px",
                  marginLeft: "25px",
                  marginBottom: "10px"
                }}
              >
                <Chart
                  data={this.state.chart_data}
                  series={{ type: "line" }}
                  axes={[
                    { primary: true, type: "linear", position: "bottom" },
                    { type: "linear", position: "left" }
                  ]}
                  primaryCursor
                  secondaryCursor
                  tooltip
                />
              </div>
            )}
          </div>
        </div>
      </div>
    );
  }
}

export default Strategy;

The appendRow function is not appending the row.

What am I missing? Is there any better way to achieve this?

Please suggest.

Thanks in advance

4
  • Can you post the code that renders the table? or the function appendRow? Commented Jan 31, 2019 at 6:49
  • @SagiRika The function appendRow is already present in the code. Check I have removed some unrelated code. Commented Jan 31, 2019 at 6:51
  • do not use jquery with React instead render your content dynamically using in array Commented Jan 31, 2019 at 6:54
  • @ShubhamKhatri Could you please post sample code for this? Commented Jan 31, 2019 at 6:55

2 Answers 2

3

You are using jquery and directly handling real DOM. With React we use Virtual DOM and don't manipulate the real DOM. Unlike Jquery, in react you don't have to worry about handling UI. Your concern should be handling the data properly, leave the UI updates to React. You haven't provided the Table Component information here. So, I would give you a code sample which does exactly what you want to achieve. For the button you can place it where it's needed within this component.

import React from "react";

class Table extends React.Component {
  state = {
    data: []
  };
  appendChild = () => {
    let { data } = this.state;
    data.push(data.length); // data.length is one more than actual length since array starts from 0.
    // Every time you call append row it adds new element to this array. 
    // You can also add objects here and use that to create row if you want.
    this.setState({data});
  };
  render() {
    return (
      <table>
        <thead>
          <th>Type</th>
          <th>Position</th>
        </thead>
        <tbody>
          {this.state.data.map(id => (
            <Row id = {id} />
          ))}
        </tbody>
      </table>
    );
  }
}

const Row = ({ id }) => (
  <tr>
    <td>
      <input type="text" id={`select-type-${id}`} />
    </td>
    <td>
      <input type="text" id={`select-position-${id}`} />
    </td>
  </tr>
);
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for your solution. This is working great. But there is one problem when we are populating the <tr>, inside that inside a <td> there is a state call like: <td><select>{this.state.price.map(c=>(<option value={c.price}>{c.price}</option>))}</select></td>. with this it gives the error: TypeError: Cannot read property 'state' of undefined
this const Row is not able access any state of the React component. So is there any way I could either populate it with the array and use it in const Row, so that the select box inside there gets populated?
That depends upon your select options. Is it different for each Row or same for everything. If its same for all the rows create a select Component and put the prices state inside that. If not : Pass it to the Row Component, considering there is a price state in Table Component. <Row id={id} price={this.state.price}> The other option is create a function that returns Row inside your component. so inside Table component create function getRow. getRow = id => (<tr><td><select>{this.state.price.map(c=>(<option value={c.price}>{c.price}</option>))}</select></td></tr>)
The value is same for all rows, and yes I just did the same thing, and it got me working. Thanks alot. Accepted the answer.
1

Constructor method:

constructor(props) {
  super(props)
  this.state = {rows: []};
}

Inside your appendRow method, instead of adding the tr directly to your tbody, add the tr to the rows state:

appendRow(event) {
  var rel = event.target.getAttribute("rel");
  rel = parseInt(rel) + 1;

  var joined = this.state.rows.concat(
  <tr>
    <td>
      <input type="text" id={`select-type` + rel} />
    </td>
    <td>
      <input type="text" id={`select-position` + rel} />
    </td>
  </tr>
  );
  this.setState({ rows: joined })
}

Inside your render method, just put the array in. It will be re-rendered whenever setState is called:

render() {
  ...
  return (
     ...
     <button
       rel="1"
       type="button"
       id="addbtn"
       className="btn btn-circle"
       onClick={this.appendRow}>
       <i className="fa fa-plus" />
     </button>
     ...
     <tbody>
       {this.state.rows}
     </tbody>
     ...
  )
  ...
}

5 Comments

On button click it gives error - TypeError: Cannot read property 'state' of undefined At line var joined=...
Apologies, check updated answer. Also check live example. codesandbox.io/s/m72w036oyx
codesandbox is full of errors ( not working fyi)
@TomStickel Just tested it now in Chrome.. seems to work fine?
failed to fetch , and ModuleNotFoundError: Could not find module in path: '@babel/runtime/helpers/interopRequireDefault' relative to '/src/index.js'

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.