Search code examples
reactjsreactjs-flux

React - how can I get updated data to show in my table?


I need to be able to update the values in my table rows and then have those new values show in the cells. How would I go about doing this?

Here is the code I am currently working with:

Main Table Component

import React from 'react';
import TableWithDataHeader from './TableWithDataHeader.jsx';
import TableWithDataBody from './TableWithDataBody.jsx';
import TableWithDataRowForm from './TableWithDataRowForm.jsx';
import {updateRowHistory} from '../../actions/DALIActions';
import AppStore from '../../stores/AppStore';

export default class TableWithData extends React.Component {
    state = {rows: [], isEditing: false, input: null};
    updateState = () => {
        let rows = this.state.rows;
        rows.shift();
        rows.push({id: AppStore.getRowId(), cells: AppStore.getUpdatedCells()});
        this.setState({rows});
        console.log(rows);
    };

    componentDidMount() {
        let rows = this.state.rows;
        rows.push({id: AppStore.getRowId(), cells: AppStore.getCells().historycells});
        this.setState({rows});
        console.log(rows);
        AppStore.addChangeListener(this.updateState);
    }

    handleEdit = (row) => {
        this.setState({isEditing: true});
    };

    handleInputChange = (newCellValuesArray) => {
        let input = this.state.input;
        input = newCellValuesArray;
        this.setState({input});
    };

    editStop = (row) => {
        this.setState({isEditing: false});
    };

    handleSubmit = (access_token, row_id) => {
        let newCellValuesArray = this.state.input;
        updateRowHistory(access_token, row_id, newCellValuesArray);
        this.setState({isEditing: false});
    };

    componentWillUnmount() {
        AppStore.removeChangeListener(this.updateState);
    }

    render() {

        let {rows, isEditing, input} = this.state;

        console.log(rows);
        console.log(rows.map(row => {
            return row.cells;
        }));

        return (
            <div>
                <div className="row">
                    <table className="table table-striped">
                        <thead>
                            <TableWithDataHeader />
                        </thead>
                        <tbody>
                            {rows.map(row => this.state.isEditing ? 
                                <TableWithDataRowForm key={row.id} cells={row.cells} editStop={this.editStop.bind(null, row)} handleSubmit={this.handleSubmit.bind(this)} handleInputChange={this.handleInputChange.bind(this)} /> : 
                                <TableWithDataBody key={row.id} cells={row.cells} handleEdit={this.handleEdit.bind(null, row)} />
                            )}
                        </tbody>
                    </table>
                </div>
            </div>
        );
    }
}

Edit Row Component

import React from 'react';
import AppStore from '../../stores/AppStore';

export default class TableWithDataRowForm extends React.Component {
    state = {cells: this.props.cells, newCellValues: []};

    onChange(e) {
        let newCellValues = this.state.newCellValues;
        newCellValues[e.target.id] = e.target.value;
        this.setState({newCellValues});
        console.log(newCellValues);
        let newCellValuesArray = [];
        for (let key in newCellValues) {
            if (newCellValues.hasOwnProperty(key)) {
                newCellValuesArray.push({contents: newCellValues[key]});
            }
        }
        console.log(newCellValuesArray);
        this.props.handleInputChange(newCellValuesArray);
    }

    editStop() {
        this.props.editStop();
    }

    handleSubmit(e) {
        e.preventDefault();

        let access_token = AppStore.getToken();
        let row_id = AppStore.getRowId();

        this.props.handleSubmit(access_token, row_id);
    }

    render() {

        let {cells, newCellValues} = this.state;

        return (
            <tr>
                {cells.map(cell => {
                    return <td key={cell.id} className="text-center"><input type="text" className="form-control" id={cell.id} defaultValue={cell.contents} onChange={this.onChange.bind(this)} /></td>
                })}
                <td>
                    <button className="btn btn-default"><i className="fa fa-ban" onClick={this.editStop.bind(this)}></i>Cancel</button>
                    <button className="btn btn-success"><i className="fa fa-cloud" onClick={this.handleSubmit.bind(this)}></i>Save</button>
                </td>
            </tr>
        );
    }
}

It's it bit mangled at the moment, but I think that you can get the general idea of what I am attempting! So, I can get the table to initially render with data values from my store and I can successfully edit them to different values. However, I would like it so that when I click my save button the new values show. I am using React with flux to build this.

Answers with examples are always much appreciated

Thanks for your time


Solution

  • Your problem is that you have the state of our cells twice. Once in your row and once in your table. You should never do this but have the state only in the table and pass them as prop and access them as prop. Only the temporary edited vakue should be saved as an extra state. You can get the prop changes via componentWillReceiveProps.

    Here an stripped down example:

    var Row = React.createClass({
    
        getInitialState: function() {
            return {
                tempValue: this.props.value 
            }
        },
    
        componentWillReceiveProps: function(nextProps) {
            //here u might want to check if u are currently editing but u get the idea -- maybe u want to reset it to the current prop on some cancelEdit method instead
            this.setState({
                tempValue: nextProps.value 
            });
         },
    
         render: function() {
            return <div><input type="text" value={this.state.tempValue} onChange={this.onChange} /></div>;
         },
    
         onChange: function(e) {
            this.setState({
                tempValue: e.target.value
            });
         }
    });
    
    
    var Hello = React.createClass({
    
        getInitialState: function() {
            return {
                value: 'someServerState' 
            }
        },
    
        render: function() {
           return (
             <div>
               <Row value={this.state.value} />
               <button onClick={this.reloadFromServer} >reload from Server</button>
             </div>
           );
        },
    
        //this will be triggered by some of ur events - i added a button
        reloadFromServer: function() {
           this.setState({
              value: 'someServerState changed somehow' 
           });
        }
    });
    

    see: https://jsfiddle.net/69z2wepo/34292/