3

This is a conceptual question in that I'm trying to understand the best way to handle tabular data in react without using any special component or library.

I have data in a html table created dynamically in my child component. The data comes from the parent component. Some of the columns have editable content that I trigger with an edit button to re-render a version of the table that has inline text boxes for all rows of the columns that are editable.

When I change the content of the text box, I want to be able to click on my save button and have all the rows get saved.

The save and edit buttons are not inline on the table, but just sit outside the table in my component.

How do I access the html table in my child component from the the parent component to read all the rows and save the values in the textboxes to a data store?

Here is a snippet of code where I'm attempting to build the select list dynamically. I'm having trouble with some syntax errors and it is not working, but it gives an idea of what I'm trying to do.

I'm passing in the category and the transaction id. I want to add the select list to each category cell in every row in my table when the edit mode is selected. The transaction id is my solution for having the index of the current row available on the list by adding 1 to the transaction id. I will then use the selected index - 1 to get the transaction id for updating the corresponding records category. This may be a hack, but I can't think of the right way or better way to link the transaction to the select list.

renderCategory(category,transid){

    console.log(category);

    if (this.props.editMode === true){

    return  <div>
        <select id="selCategory" onChange={this.props.onCategoryChange}>

  const arrCategory = ["Category1","Category1","Category2","Category3","Category4","Category5","Category6","Category7"];

          var i;

          for (i = 1;arrCategory.length+1;i++){

            <option value={transid+1}>{arrCategory[i-1]}</option>

            if(arrCategory[i-1] === category) {
              selectedIndex = i-1;
            }
          }
        </select>
      </div>
    }
    else {
      return category
    }
  }

Here I have the code in the parent for handling the onChange event of the select list in my child.

  handleCategoryChange(e) {

         // this will have to be changed because I'm using the index value to store the transaction id

        alert("The Child HTML is: " + e.target.innerHTML + "The selected value is: " + e.target.options[e.target.selectedIndex].value); 


        //// update the date which is in parent state to reflect the new value for category for the passed in transaction id (the problem is I don't know what the transaction id is...)

      }
mo_maat
  • 2,110
  • 12
  • 44
  • 72

1 Answers1

2

To achieve this, you need to do the following

In your parent component:

  • Maintain a state in your parent component which will store the data that has to be rendered in the child component.
  • Write a function in parent component which will update the state(i.e. the data to be rendered in the child component).

  • Then pass the data in your parent component's state and the state update function to child component via props.

In your child component:

  • Retrieve the data and the function passed by the parent component from the props of the child component.

  • Use the data to populate your table and to each editable box's input, pass an onChange and provide it the reference of the function passed from your parent component.

Here is a small snippet to take reference from:

class Parent extends Component {

    constructor() {
        super()
        this.state = {
            data: ''
        }
    }

    //update your state here
    stateUpdateHandler = (value) => {            
        this.setState({data: value})
    }

    render () {
        return(
            <Child data={this.state.data} stateUpdateHandler={stateUpdateHandler} />
        )
    }

}


class Child extends Component {

    render() {
        const {data, stateUpdateHandler}
        return(
            <div>
                <input 
                  type='text' value={d.value} 
                  onChange={(e) => stateUpdateHandler(e.target.value)} />
            </div>
        )
    }

}

EDIT: This is how you should handle onChange event

onCategoryChange: function(event) {
    console.log(event.target.value) //This would have the value of transaction provided in the value attribute of option tag
}

If you want to get the transaction id and not the value in the value attribute of the option tag, you will have to change your select tag and write it like this:

<select id="" onChange={function() {this.props.onCategoryChange(transid)}}>
Swapnil
  • 2,573
  • 19
  • 30
  • Great. Thanks. But how do you capture a row index in the case where for example the element triggering the onChange is a select list that is nested in a table cell? The first cell of the table in the child component has a id value that is the row for which the update should apply. – mo_maat Dec 15 '16 at 17:11
  • I suppose you will be using [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) to loop over the data and render the table rows. In this map function you can pass a second parameter index to get the index number. [1,2,3].map(val, index => console.log(index) ) You can check out [this](http://stackoverflow.com/a/38364482/3920820) answer as well if what I explained is not clear – Swapnil Dec 15 '16 at 17:45
  • I added some sample code to explain where I'm stuck and give some idea of the "conceptual" issue I'm having. Basically, how do I tie the transaction id of the row to the list. I can't do this because the onChange is triggered by the list and does not contain any information on the row/transaction id that the list belongs to. – mo_maat Dec 15 '16 at 20:20
  • Added an edit. This should help if I understood the problem correctly – Swapnil Dec 15 '16 at 20:43
  • I see what you are suggesting. Let me play with it and see if I can work it out. Thanks! – mo_maat Dec 15 '16 at 20:52
  • Ok. So how do it access the transid passed in here ` – mo_maat Dec 15 '16 at 21:57
  • I get the error: I get the error: `Uncaught TypeError: Cannot read property 'value' of undefined` when I try to access the even target using `event.target.value`. And when I tyr to display `event.target` in the console I just get `undefined`. – mo_maat Dec 15 '16 at 22:04
  • Ok. So I think I figured it out based on your pointers. passing a function in to the onChange overrides the default event that gets passed to the function on the parent. So what is passed in is actually just the transaction id (transid). Console.log(event) gives 2416 (transid). Now the problem is how do I access the value of the select list item if there is no event to read it from? – mo_maat Dec 15 '16 at 22:35
  • Can you point me to your code base. I think I will be able to understand the problem if I run the odebase myself – Swapnil Dec 16 '16 at 04:46