React - toggling input in a cell of a table - reactjs

I have a table where I would like it to be possible to click on a cell and for that to toggle an input that allows you to change the data in that cell. At the moment I am just rendering the input straight off the bat but ideally I don't want this. What I would really like is to show the current data in the cells and then when you click on the cell it becomes the input and allows you to edit the data.
Table Component:
import React from 'react';
import TableWithDataHeader from './TableWithDataHeader.jsx';
import Row from './Row.jsx';
import {createRowHistory} from '../../actions/DALIActions';
import TokenStore from '../../stores/TokenStore';
import TableDataStore from '../../stores/TableDataStore';
export default class Table extends React.Component {
state = {data: TableDataStore.getCells().historycells};
handleSubmitEvent = (e) => {
e.preventDefault();
console.log(this.state.data);
let data = this.state.data;
let dataEntriesArray = [];
for (let key in data) {
if (data.hasOwnProperty(key)) {
dataEntriesArray.push({contents: data[key]});
}
}
console.log(dataEntriesArray);
let access_token = TokenStore.getToken();
let row_id = TableDataStore.getRowId();
createRowHistory(access_token, row_id, dataEntriesArray);
};
handleChangeEvent = (value, cell) => {
let newState = this.state.data.slice(0);
console.log(newState);
newState[cell] = value;
console.log(newState);
this.setState({data: newState});
console.log(this.state.data);
};
render() {
let {data} = this.state;
return (
<div>
<div className="row">
<table className="table table-striped table-bordered">
<thead>
<TableWithDataHeader />
</thead>
<tbody>
<Row data={this.state.data} handleSubmitEvent={this.handleSubmitEvent} handleChangeEvent={this.handleChangeEvent} />
</tbody>
</table>
</div>
</div>
);
}
}
Row Component:
import React from 'react';
import TableDataStore from '../../stores/TableDataStore';
export default class Row extends React.Component {
render() {
let cells = this.props.data.map((el, i) => {
return (
<td key={i}>
<input type="text" className="form-control" id={el.id} defaultValue={el.contents} onChange={(e) => {
this.props.handleChangeEvent(e.target.value, i)
}} />
</td>
);
});
cells.push(
<td className="dtable-button" key={this.props.data.length}>
<button className="btn btn-primary" onClick={this.props.handleSubmitEvent}>Submit</button>
</td>
);
return (
<tr id={TableDataStore.getRowId()}>{cells}</tr>
);
}
}
Can I just simply toggle a state that will switch between a default show state (a <div>?) and the input that I already have? Or do I need to do something a little more involved?
Any help would be much appreciated!
Thanks for your time

One approach would be to create a Cell component that renders a div, but when clicked, renders an input instead.
import React from 'react';
class Cell extends React.Component {
constructor(props) {
super(props);
this.state = { editing: false };
}
render() {
const { value, onChange } = this.props;
return this.state.editing ?
<input ref='input' value={value} onChange={e => onChange(e.target.value)} onBlur={() => this.onBlur()} /> :
<div onClick={() => this.onFocus()}>{value}</div>
}
onFocus() {
this.setState({ editing: true }, () => {
this.refs.input.focus();
});
}
onBlur() {
this.setState({ editing: false });
}
}
Then you can create your table rows like this:
let cells = data.map((el, i) => (
<td key={i}>
<Cell
value={el.contents}
onChange={v => { this.props.handleChangeEvent(v, i) }}
/>
</td>
));

You can it like these.
let cells = this.props.data.map((el, i) => {
return (
<td key={i}>
{
(el.editable)?
<input type="text" className="form-control" id={el.id} defaultValue={el.contents} onChange={(e) => {
this.props.handleChangeEvent(e.target.value, i)
}} />:
<div onClick={onCellClick(i){}}>{el.contents}</div>
}
</td>
);
});
editable flag should be toggled using onCellClick(i){} & this.props.handleChangeEvent(e.target.value, i) functions. u should update the approprite state based on the index you have here

Related

Is there something wrong with my react parent class?

I am practicing using react to build a simple table. Here my table has three columns. (name, job, delete). There is already some data in the table. In the third column, I want to build a button so the user can click and cancel the whole row
I already fixed several bugs but the table still does not show up
const TableBody = props => {
const rows = props.fillTheData.map((row, index) => {
return (
<tr key={index}>
<td>{row.name}</td>
<td>{row.job}</td>
<td><button onClick={() => props.removeCharacter(index)}>Delete</button></td>
</tr>
);
});
return <tbody>{rows}</tbody>;
}
class App extends React.Component {
state = {
character : [ ]
};
removeCharacter = index => {
const {character} = this.state;
this.setState({
character: character.filter((element, i) => {
return i !== index;
})
});
}
handleSubmit = character => {
this.setState({character:[...this.state.character,character]})
}
render() {
return(
<div class= "container">
<Table characters = {this.state.character} removeCharacter = {this.removeCharacter} />
<Form handleSubmit = {this.handleSubmit}/>
</div>
)
}
}
class Form extends React.Component {
constructor (props) {
super( props );
this.initialState = {
name: '',
job: ''
};
this.state = this.initialState;
}
handleChange = event => {
const{name,job} = event.target;
this.setState(
{
[name]: value
}
);
}
submitForm = () => {
this.props.handleSubmit(this.state);
this.setState(this.initialState);
}
render() {
const { name, job } = this.state;
return (
<div class="container2">
<form>
<label>Name</label>
<input
type="text"
name="name"
value={name}
onChange={this.handleChange} />
<label>Job</label>
<input
type="text"
name="job"
value={job}
onChange={this.handleChange}/>
</form>
<input
type="button"
value="Submit"
onClick={this.submitForm} />
</div>
);
}
}
export default Form;
class Table extends React.Component {
render(){
const {characters, removeCharacter} = this.props;
return(
<table>
<TableHeader />
<TableBody fillTheData = {characters} removeCharacter= {removeCharacter} />
</table>
)
}
}
const TableHeader = () => {
return (
<thead>
<tr>
<th>Name</th>
<th>Job</th>
<th>Delete</th>
</tr>
</thead>
);
}
Right now, we have a cool Table component, but the data is being hard-coded. One of the big deals about React is how it handles data, and it does so with properties, referred to as props, and with state. First, we’ll focus on handling data with props.
Then let’s move all that data to an array of objects, as if we were bringing in a JSON-based API. We’ll have to create this array inside our render().
The handleChange method of Form component is declaring a constant named job which doesn't exist in event.target object and it is setting the state with value variable which doesn't exists in that scope.
So change
const{name,job} = event.target;
to
const{name,value} = event.target;
your code will just work fine.

Component Not Updating React/Redux

I am using Redux on my app. I have an Items component connected to redux. It passes down props to individual Item components.I also have a ItemEditModal component that edits the individual component. The problem i'm facing is that when i edit an Item through the ItemEditModal, the Items component does not update eventhough the redux did change. Here is relevant parts of my code:
my reducer file ..
this is the action to focus on: actionTypes.SET_EDIT_SELECTED_ITEM
const reducer = (state=initialState, action) => {
switch(action.type) {
case (actionTypes.SET_EDIT_SELECTED_ITEM):
let set_edit_selected_item_arr = {...state};
set_edit_selected_item_arr = set_edit_selected_item_arr.items.map(item => {
if (item.id === action.editInfo.id ) {
item.color = action.editInfo.color;
item.size = action.editInfo.size;
item.quantity = action.editInfo.quantity;
return item;
} else {
return item;
}
});
return {
...state,
items: set_edit_selected_item_arr
};
default:
return state;
}
}
my Items Component
import Item from '../../components/Item';
import './Items.css';
class Items extends Component {
render() {
return (
<table className="Items">
<thead>
<tr className="Items__header">
<th className="item_col">{this.props.items.length} ITEMS</th>
<th className="size_col">SIZE</th>
<th className="qty_col">QTY</th>
<th className="price_col">PRICE</th>
</tr>
</thead>
<tbody>
{this.props.items.map(item => {
return (<Item item={item} key={item.name} />);
})}
</tbody>
</table>
)
}
}
const mapStateToProps = (state) => {
return {
items: state.itemRedux.items
}
}
export default connect(mapStateToProps)(Items);
my ItemEditModal component
import './ItemEditModal.css';
class ItemEditModal extends Component {
state = {
id: this.props.itemRedux.selectedItem.id,
size: this.props.itemRedux.selectedItem.size,
color: this.props.itemRedux.selectedItem.color,
quantity: this.props.itemRedux.selectedItem.quantity,
}
onUnselectItemFunc = () => {
this.props.onSetSelectedItem(null);
this.props.onSetEditItemMode(false);
}
onQuantityChange = (e) => {
//validation required
this.setState({quantity: e.target.value})
}
onSelectChange = (e) => {
this.setState({size: e.target.value})
}
onColorChange = (newColor) => {
this.setState({color: newColor})
}
onSubmitForm = (e) => {
e.preventDefault();
this.props.onSetEditSelectedItem(this.state);
this.props.onSetEditItemMode(false);
}
render() {
return (
<div className={"ItemEdit " + (this.props.itemRedux.editItemMode ? 'showModal': '')}>
<div className="ItemEdit__close">
<span onClick={this.onUnselectItemFunc}>x</span>
</div>
<div className="ItemEdit__container">
<div className="ItemEdit__info">
<h4>{this.props.itemRedux.selectedItem.name}</h4>
<h2>$ {this.props.itemRedux.selectedItem.price}</h2>
<p className="ItemEdit__styleNum">{this.props.itemRedux.selectedItem.styleNum}</p>
<p className="ItemEdit__control-color">
<span
className="ItemEdit__control-color--red"
onClick={()=> {this.onColorChange('red')}}>
</span>
<span
className="ItemEdit__control-color--green"
onClick={()=> {this.onColorChange('green')}}>
</span>
<span
className="ItemEdit__control-color--blue"
onClick={()=> {this.onColorChange('blue')}}>
</span>
</p>
<p>Color: {this.state.color}</p>
<form onSubmit={this.onSubmitForm}>
<select onChange={this.onSelectChange}>
<option value="S">small</option>
<option value="M">medium</option>
<option value="L">large</option>
</select>
<input
type="number"
maxLength="2"
defaultValue={this.props.itemRedux.selectedItem.quantity}
onChange={this.onQuantityChange}
/><br/>
<button className="btn btn-primary">EDIT</button>
</form>
Check product details
</div>
<div className="ItemEdit__img">
<img src={this.props.itemRedux.selectedItem.imgUrl} alt="shirt pic" />
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
itemRedux: state.itemRedux
}
}
const mapDispatchToProps = (dispatch) => {
return {
onSetSelectedItem: (bool) => dispatch(actions.setSelectedItem(bool)),
onSetEditItemMode: (bool) => dispatch(actions.setEditItemMode(bool)),
onSetEditSelectedItem: (itemInfo) => dispatch(actions.setEditSelectedItem(itemInfo))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ItemEditModal);
Maybe i'm not seeing something. thanks in advance.
Nothing happens when I dispatch an action
Sometimes, you are trying to dispatch an action, but your view does not update. Why does this happen? There may be several reasons for this.
Never mutate reducer arguments
It is tempting to modify the state or action passed to you by Redux. Don't do this!
Redux assumes that you never mutate the objects it gives to you in the reducer. Every single time, you must return the new state object. Even if you don't use a library like Immutable, you need to completely avoid mutation
Source: https://redux.js.org/troubleshooting#nothing-happens-when-i-dispatch-an-action

Editable Form ReactJS

import React, { Component } from 'react';
let _ = require('lodash');
import {bindActionCreators} from "redux";
import {connect} from 'react-redux';
import {fetchedBeaconsEdit} from '../../actions/';
import {editBeacon} from '../../actions/index';
// TODO - come up with a decent name
class InfoRow extends Component {
render() {
return (
<tr>
<td>
{ this.props.beacon === "name"
|| this.props.beacon === "major"
|| this.props.beacon === "minor"
|| this.props.beacon === "beaconType" ?
<span>{this.props.beacon}<span className="font-css top">*</span></span>:
<span>{this.props.beacon}</span>
}
</td>
<td>
{ this.props.beacon !== "beaconType" &&
this.props.beacon !== "uuid" &&
this.props.beacon !== "status" &&
this.props.beacon !== "store"&&
this.props.beacon !== "group" ?
<div>
<input type="text"
className="form-control"
defaultValue={this.props.beaconValue}
name={this.props.beaconValue}
onChange={(e) =>this.props.handleInputChangeProp(e.target.value)}
/></div>:
this.props.beacon === "uuid" && this.props.beacon === "status" && this.props.beacon=== "store"?
<span></span>:
this.props.beacon === "beaconType"?
<select defaultValue={this.props.beaconValue} name={this.props.beaconValue} className="form-control" onChange={(e) =>this.props.handleInputChangeProp(e.target.value)}>
<option name="ibeacon">IBEACON</option>
<option name="eddystone">EDDYSTONE</option>
</select>:this.props.beaconValue
}
</td>
</tr>
)
}
}
class BeaconEdit extends Component {
constructor(props){
super(props);
this.state = {
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleInputChange(beacon, value) {
this.setState({
[beacon]: value
});
}
handleClick = () =>{
Object.keys(this.props.ebcn).map((key)=> {
if (this.state[key] !== undefined) {
this.props.editBeaconGroup[key]=this.state[key];
}
})
this.props.handleSubmitProp(this.props.editBeaconGroup);
}
render() {
const rows = [];
let a = this.props.ebcn;
Object.keys(this.props.ebcn).map((keyName, keyIndex) =>{
if (keyName === "store" || keyName === "group") {
return rows.push(<InfoRow beacon={keyName} beaconValue={a[keyName].name.toString()} name={this.state[keyName]} key={keyIndex} handleInputChangeProp={(inp) =>this.handleInputChange(keyName, inp)}/>);
}else{
return rows.push(<InfoRow beacon={keyName} beaconValue={a[keyName].toString()} name={this.state[keyName]} key={keyIndex} handleInputChangeProp={(inp) =>this.handleInputChange(keyName, inp)}/>);
}
});
return (
<div className="col-md-6">
<div className="">
<table className="table table-clear">
<tbody>
{rows}
</tbody>
</table>
</div>
<div className="px-1" >
<button className="btn btn-sm btn-info btn-size btn-block" onClick={this.handleClick}>Save</button>
</div>
</div>
)
}
}
class BeaconDetailEditComponent extends Component {
constructor(props){
super(props);
this.state = {
editbeacons: {}
};
this.handleSubmit = this.handleSubmit.bind(this);
this.validateName = this.validateName.bind(this);
this.validateMajor = this.validateMajor.bind(this);
this.validateMinor = this.validateMinor.bind(this);
}
validateMinor = (minor) => {
var re = /^[0-9]+$/;
return re.test(minor);
}
validateMajor = (major) => {
var re = /^[0-9]+$/;
return re.test(major);
}
validateName = (name) => {
var re = /^[A-Za-z ]+$/;
return re.test(name);
};
handleSubmit (beaconedited) {
console.log(beaconedited.name);
if (!this.validateName(beaconedited.name)) {
alert('Name can not be an integer')
}
else if (!this.validateMajor(beaconedited.major)) {
alert('Major number can only be an integer')
}
else if (beaconedited.major.length > 5) {
alert('Major number can not exceed 5 digits')
}
else if (!this.validateMinor(beaconedited.minor)) {
alert('Minor number can only be an integer')
}
else if (beaconedited.major > 65535) {
alert('Major number can not exceed the limit of 65535')
}
else if (beaconedited.minor > 65535) {
alert('Minor number can not exceed the limit of 65535')
}
else {
this.props.editBeacon(beaconedited, this.props.location.query.id);
}
}
componentWillMount = () => {
this.props.fetchedBeaconsEdit(this.props.location.query.id);
};
render() {
return (
<div className="container px-3 mr-3">
<div>
<div className="col-md-6 col-md-offset-3"><h1>Edit Beacon Information</h1></div>
</div>
<br/>
<br/>
{ this.props.ebcn != null?
<div>
<BeaconEdit ebcn={this.props.ebcn} handleSubmitProp={this.handleSubmit} editBeaconGroup={this.state.editbeacons}/>
</div> :
<center><img className="gif-size" src={'img/avatars/default.gif'} alt="Loading"/></center>
}
</div>
)
}
}
function mapStateToProps(state) {
return {
eBeacon: state.eBeacon,
ebcn: state.beacons
}
}
function matchDispatchToProps(dispatch){
return bindActionCreators({editBeacon: editBeacon, fetchedBeaconsEdit: fetchedBeaconsEdit}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(BeaconDetailEditComponent);
i had provided the code snippet
what i am doing is
i had fetched the values from the server and were shown in the fields and I'm making this page as editable form
what i want to do is now to take the values changed or changed by the user and to print them in console.
i had applied handleInputChange and its showing changed values while changing but i want to see those values in console on button click as well
how to do it?
First, the manner your are changing the state is not good. You are setting the new state to have only a single attribute which is the name and value of the field which is modified.
Coming to how to print the value of the field you are modifying, you should do a console log in the handle change method.
That said, your code should look like:
handleInputChange(event) {
//const {name, value} = event.target;
//const oldState = this.state;
//const newState = Object.assign({},oldState,{[name]:value});
//this.setState(newState);
this.setState({
[event.target.name]: event.target.value
});
//printing the input name and value as it is being modified.
console.log(name + ":" + value);
//if you want to see the value of the current state uncomment the line below
//console.log("State=" + JSON.stringify(newState));
}
printState = () => {
console.log("State="+JSON.stringify(this.state));
}
For more insight on how to modify objects with assign method see: MDN doc on Object.assign()
If you want to print the current state on clicking the save button then add the onClick attribute to the button as shown in the code below:
<button value="Save" onClick={this.printState}/>
Change InfoRow to
class InfoRow extends Component {
render() {
return (
<tr>
<td>
{this.props.beacon}
</td>
<td>
<input type="text"
className="form-control"
defaultValue={this.props.beaconValue}
value={this.props.name}
name={this.props.beaconValue}
onChange={(e) =>this.props.handleInputChangeProp(e.target.value)}
/>
</td>
</tr>
)
}
}
and render to
Object.keys(this.props.ebcn).map((keyName, keyIndex) =>{
return rows.push(<InfoRow beacon={keyName} beaconValue={a[keyName].toString()} name={this.state[keyName]} key={keyIndex} handleInputChangeProp={(inp) =>this.handleInputChange(keyName, inp)}/>);
});
and
handleInputChange(beacon, value) {
this.setState({
[beacon]: value
});
}
and handleClick to
handleClick = () =>{
Object.keys(this.props.ebcn).map((key)=> {
console.log(this.state[key]);
}))
}

React - How can I get a re-render to happen in my table?

I have a table which starts off with data from a store which can then be edited by row. When the editing has been completed, a button is clicked which saves the new values and should present them in the table. However, this is not happening for me. I have been stuck on this for a while now and have tried various things but none of them have worked. I think it may be a problem with the state but I can't figure out how to change it to make it work!
The code I have currently is:
Table:
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};
componentDidMount() {
let rows = this.state.rows;
rows.push({id: AppStore.getRowId(), cells: AppStore.getCells().historycells});
this.setState({rows});
console.log(rows);
}
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});
};
render() {
let {rows, isEditing, input} = this.state;
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.id)}
handleSubmit={this.handleSubmit}
handleInputChange={this.handleInputChange}
/>
:
<TableWithDataBody
key={row.id}
cells={row.cells}
handleEdit={this.handleEdit.bind(null, row.id)}
/>
)}
</tbody>
</table>
</div>
</div>
);
}
}
Data table starts with:
import React from 'react';
export default class TableWithDataBody extends React.Component {
state = {cells: this.props.cells};
handleEdit = () => {
this.props.handleEdit();
};
render() {
let {cells} = this.state;
return (
<tr>
{cells.map(cell => {
return <td key={cell.id} className="text-center">{cell.contents}</td>
})}
<td>
<button className="btn btn-primary" onClick={this.handleEdit}><i className="fa fa-pencil"></i>Edit</button>
</td>
</tr>
);
}
}
In-row edit form:
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} /></td>
})}
<td>
<button className="btn btn-default"><i className="fa fa-ban" onClick={this.editStop}></i>Cancel</button>
<button className="btn btn-success"><i className="fa fa-cloud" onClick={this.handleSubmit}></i>Save</button>
</td>
</tr>
);
}
}
Help with examples would be much appreciated, sorry if the code is a bit of a mess right now!
Thanks for your time
I might be missing something, but I don't see where you're editing the rows state? The change handler just changes the input, and you aren't passing the input down to the data table.
The only time I see rows being set is in componentDidMount, which explains why it's being populated, but not changed.

React - require some aid with saving data to a table

I have a table with dynamically generated column headers and rows, a user can enter in data to the rows and when they click save the data should be saved into that row but at the moment it isn't saving the values to the table but it is saving them on my database. Ideally I would like it so that when the save button is clicked the data will be saved to the row and is then viewable in that row (if that makes any sense).
Here is the code I am working with (I know it is a mess at the moment!):
Code for data input form:
import React from 'react';
import AppStore from '../../stores/AppStore';
export default class RowForm extends React.Component {
state = {dataEntries: []};
onChange = (event, element) => {
let dataEntries = this.state.dataEntries;
dataEntries[element] = event.target.value;
this.setState({dataEntries});
};
editStop = () => {
this.props.editStop();
};
handleSubmit = (e) => {
e.preventDefault();
let access_token = AppStore.getToken();
let id = AppStore.getTable().id;
let dataEntries = this.state.dataEntries;
let dataEntriesArray = [];
for (let key in dataEntries) {
if (dataEntries.hasOwnProperty(key)) {
dataEntriesArray.push({contents: dataEntries[key]});
}
}
this.props.handleSubmit(access_token, id, dataEntriesArray);
};
componentDidMount() {
let nameArray = AppStore.getTable().columns.map((obj) => {
return obj.name;
});
let dataEntries = nameArray.reduce((obj, name) => {
obj[name] = null;
return obj;
}, {});
this.setState({dataEntries});
}
render() {
let {dataEntries} = this.state;
return (
<tr>
{Object.keys(dataEntries).map((element) => {
return (
<td key={element}><input type="text" className="form-control" id={element} placeholder="enter data" value={dataEntries[element]} onChange={event => this.onChange(event, element)} /></td>
);
})}
<td>
<button className="btn btn-default" onClick={this.editStop}><i className="fa fa-ban"></i>Cancel</button>
<button className="btn btn-success" onClick={this.handleSubmit}><i className="fa fa-check"></i>Save</button>
</td>
</tr>
);
}
After the data has been entered and submitted (it is an array of objects like dataEntriesArray = [{contents: "value"}, {contents: "value"}, {contents: "value"}, {contents: "value"}].
And here is how I am rendering the table (this is where the problem is I think):
import React from 'react';
import TableHeader from './TableHeader.jsx';
import RowForm from './RowForm.jsx';
import {createRow} from '../../actions/DALIActions';
import AppStore from '../../stores/AppStore';
export default class Table extends React.Component {
state = {rows: [], isNew: false, isEditing: false};
handleAddRowClickEvent = () => {
let rows = this.state.rows;
rows.push({isNew: true});
this.setState({rows: rows, isEditing: false});
};
handleEdit = (row) => {
this.setState({isEditing: true});
};
editStop = () => {
this.setState({isEditing: false});
};
handleSubmit = (access_token, id, dataEntriesArray) => {
createRow(access_token, id, dataEntriesArray);
};
render() {
let {rows, isNew, isEditing} = this.state;
let headerArray = AppStore.getTable().columns;
return (
<div>
<div className="row" id="table-row">
<table className="table table-striped">
<thead>
<TableHeader />
</thead>
<tbody>
{rows.map((row, index) => this.state.isEditing ?
<RowForm formKey={index} key={index} editStop={this.editStop} handleSubmit={this.handleSubmit} /> :
<tr key={index}>
{headerArray.map((element, index) => {
return (
<td key={index} id={element.id}></td>
);
})}
<td>
<button className="btn btn-primary" onClick={this.handleEdit.bind(this, row)}><i className="fa fa-pencil"></i>Edit</button>
</td>
</tr>)}
</tbody>
</table>
</div>
<div className="row">
<div className="col-xs-12 de-button">
<button type="button" className="btn btn-success" onClick={this.handleAddRowClickEvent}>Add Row</button>
</div>
</div>
</div>
);
}
}
I am using flux at the moment, and would ideally like to keep using it for now (I know about redux but I would ideally like to get it working in flux before I start refactoring my code). I suspect it is a problem with the way I am rendering my table.
Any help would be much appreciated, especially examples!
Thanks for you time!
It looks like you probably want to extract your table data into your store, your UI child elements trigger change events, your store then updates its data and trigger change events which your parent component can listen for and update.
Something like the following simplified example, which mutates array elements:
class Store extends EventEmitter {
constructor() {
super()
this.data = [ 'a', 'b', 'c' ]
}
onChange() {
this.emit( 'update', this.data )
}
mutate( index, value ) {
this.data[ index ] = value
this.onChange()
}
}
var store = new Store()
class ChildComponent extends React.Component {
constructor( props ) {
super( props )
}
// You probably want to use a dispatcher rather than directly accessing the store
onClick = event => {
store.mutate( this.props.index, this.props.value + 'Z' )
}
render() {
return <button onClick={ this.onClick }>{ this.props.value }</button>
}
}
class ParentComponent extends React.Component {
constructor( props ) {
super( props )
// You probably want to be smarter about initially populating state
this.state = {
data: store.data
}
}
componentWillMount() {
store.on( 'update', data => this.setState({ data: data }) )
}
render() {
let cells = this.state.data.map( ( value, index ) => <ChildComponent index={ index } value={ value } /> )
return (
<div>
{ cells }
</div>
)
}
}
For brevity child components here directly tell the store to change values, you'd probably want to dispatch messages/actions and have the store decide how to respond, the key is simply that the store data is passed to the parent component, which updates its state and triggers a re-render.
The flow here is that essentially the UI is dumb, it simply renders the data that it gathers from the store and dispatches a message to tell the store to update/mutate when a user action is detected (in this case a button press but it sound like you'll need an input of some sort), when the data in the store changes it emits (or could use a dispatcher also) a change event which forces the UI to re-render the new state. As child components are re-rendered at this stage they are populated with the new data state, ensuring that your UI remains consistent.

Resources