I am building an application that requires a table of items to be sorted and change the orderNumber of them depending on their sorting. I installed and utilized a library called react-dnd to handle the functionality of sorting/ordering, and its working great so far. The issue im having is the update. When a user moves one of the items, I need to send a PUT request to the api and update its orderNumber. It was working last night great, here is my code.
The ListItem (Item that is being sorted and updated):
import React, {PropTypes} from 'react';
import {Link} from 'react-router';
import {DragSource, DropTarget} from 'react-dnd';
import sdk from '../../js/sdk';
import ItemTypes from './ItemTypes';
const itemSource = {
beginDrag(props) {
return {id: props.id};
}
};
const itemTarget = {
hover(props, monitor) {
const draggedId = monitor.getItem().id;
if (draggedId !== props.id) {
props.swapItems(draggedId, props.id);
}
}
};
const DragSourceDecorator = DragSource(ItemTypes.ITEM, itemSource, (connect, monitor) => {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
};
});
const DropTargetDecorator = DropTarget(ItemTypes.ITEM, itemTarget, (connect) => {
return {connectDropTarget: connect.dropTarget()};
});
class SwagBagItem extends React.Component {
constructor(props) {
super(props);
this._handleDelete = this._handleDelete.bind(this);
}
componentWillReceiveProps(nextProps) {
const swagbagItemCpy = Object.assign({}, nextProps.swagbagItem);
delete swagbagItemCpy.id;
if (nextProps) {
sdk.put(`swagbags/${nextProps.swagbag.id}/items/${nextProps.swagbagItem.id}`, swagbagItemCpy)
.done((result) => {
console.log(result);
}).fail((error) => {
console.log(error);
})
;
}
}
_handleDelete(event) {
event.preventDefault();
event.stopPropagation();
if (confirm('Are you sure you want to delete this Swagbag Item?')) {
sdk.delete(`swagbags/${this.props.swagbag.id}/items/${this.props.swagbagItem.id}`)
.done(() => {
console.log('Swagbag Item remove!');
}).then(() => {
this.props.loadSwagBags();
});
}
}
render() {
const {swagbagItem} = this.props;
return this.props.connectDragSource(this.props.connectDropTarget(
<tr className="swagbag-item">
<td>{swagbagItem.id}</td>
<td><Link to={`${this.props.swagbag.id}/items/${swagbagItem.id}`}>{swagbagItem.name}</Link></td>
<td>{swagbagItem.uri}</td>
<td>
<div className="btn-group btn-group-xs pull-right" role="group">
<Link to={`${this.props.swagbag.id}/items/${swagbagItem.id}/edit`} className="btn btn-info">Edit</Link>
<Link to={`${this.props.swagbag.id}/items/${swagbagItem.id}`} className="btn btn-info">View</Link>
<button className="btn btn-danger btn-xs" onClick={this._handleDelete}>Remove</button>
</div>
</td>
</tr>
));
}
}
SwagBagItem.propTypes = {
loadSwagBags: PropTypes.func,
params: PropTypes.object,
swagbag: PropTypes.object,
swagbagItem: PropTypes.object,
};
export default DropTargetDecorator(DragSourceDecorator(SwagBagItem));
The container or list that holds these items:
import React, {PropTypes} from 'react';
import {Link} from 'react-router';
import {DragDropContext} from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import sdk from '../../js/sdk';
import Nav from '../Nav';
import SwagBagItem from '../SwagBagItem';
class SwagBagItemsList extends React.Component {
constructor(props) {
super(props);
this.state = {
swagbag: null,
swagbagItems: [],
};
this._loadSwagBags = this._loadSwagBags.bind(this);
this._compareItems = this._compareItems.bind(this);
this._swapItems = this._swapItems.bind(this);
}
componentWillMount() {
this._loadSwagBags();
}
_compareItems(item1, item2) {
return item1.orderNumber - item2.orderNumber;
}
_swapItems(itemNo1, itemNo2) {
const items = this.state.swagbagItems;
let item1 = items.filter(item => item.id === itemNo1)[0];
let item2 = items.filter(item => item.id === itemNo2)[0];
let item1Order = item1.orderNumber;
item1.orderNumber = item2.orderNumber;
item2.orderNumber = item1Order;
items.sort(this._compareItems);
this.setState({swagbagItems: items});
}
_loadSwagBags() {
sdk.getJSON(`swagbags/${this.props.params.id}`)
.done((result) => {
this.setState({swagbag: result});
})
.then(() => {
sdk.getJSON(`swagbags/${this.props.params.id}/items?fields=id,name,summary,uri,itemImageFile,orderNumber`).done((results) => {
this.setState({swagbagItems: results});
});
});
}
render() {
let swagbagItems = null;
if (this.state.swagbagItems) {
swagbagItems = this.state.swagbagItems.map((item) => {
return <SwagBagItem
loadSwagBags={this._loadSwagBags}
swagbag={this.state.swagbag}
swagbagItem={item}
key={item.id}
id={item.id}
swapItems={this._swapItems}
/>;
});
}
if (!this.state.swagbag) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Swagbag Items</h1>
<Nav swagbag={this.state.swagbag} />
<table className="table">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>uri</th>
<th></th>
</tr>
</thead>
<tbody>
{swagbagItems}
</tbody>
</table>
<Link to={`swagbags/createItem/swagbagid/${this.state.swagbag.id}`} className="btn btn-success">Add Item</Link>
</div>
);
}
}
SwagBagItemsList.propTypes = {
params: PropTypes.object,
};
export default DragDropContext(HTML5Backend)(SwagBagItemsList);
It is making the PUT request, but its making hundreds of them in a row from just moving one object. I cant for the life of me figure out why. This puts a severe lag on the application and makes it unresponsive. Am I going about this the right way, and if so, what is the solution to this?
EDIT #1: Woke up today and the application is working fine. Unfortunately this is going in production, so before that I have to recreate the bug of 800+ PUT requests and figure it out. Might put a bounty on this.
If you want to get it so that it sends the update once it's finished dragging, there's an endDrag function you can add to your DragSource (http://gaearon.github.io/react-dnd/docs-drag-source.html) that will only be fired once and will only be fired upon finishing the drag. So if you remove your api call from componentWillReceiveProps and move it to the source like this:
const itemSource = {
beginDrag(props) {
return {
id: props.id,
swagbagId: props.swagbag.id,
swagbagItem: props.swagbagItem,
};
},
endDrag(props, monitor) {
const item = monitor.getItem();
sdk.put(`swagbags/${item.swagbagId}/items/${item.swagbagItem.id}`, item.swagbagItem)
.done((result) => {
console.log(result);
}).fail((error) => {
console.log(error);
})
;
},
};
It should only make the call one time (I can't perfectly predict that without knowing what's in swagbag and swagbagItem but I think it should). Note that I'm using the getItem() function from the DragSource monitor (http://gaearon.github.io/react-dnd/docs-drag-source-monitor.html) to retrieve what was passed in upon beginDrag.
Related
From reading previous SO posts and blogs I'm not sure if this is related to props. Either way I'm baffled.
I have a class component, responsible for loading data, which uses a functional component for displaying the data. When the delete button, in the functional component, is pressed it calls props.onDelete which does a fetch and reloads the data. The correct row is deleted from the DB but in the browser it's always the bottom row which is removed. On reloading the page the correct data is displayed.
I've put a breakpoint in the functional component and in the class component render and loadStations methods. On clicking delete button I can see that loadStations is called (which calls setState) and then the functional component is called. However, the render method is never called.
Stations.js (the class component parent)
import React, {Component} from "react";
import EditableTable from "../util/EditableTable";
// column definitions
const columns = [
...
]
export default class Stations extends Component {
constructor() {
super();
this.state = {
stations: []
};
}
componentDidMount () {
this.loadStations();
}
loadStations() {
fetch(`/api/stations`)
.then(response => response.json())
.then(response => {
this.setState({
stations: response.data
})
});
}
saveStation = (e, station) => {
...
}
deleteStation = (e, dataRowIdx) => {
e.preventDefault();
var stationId = this.state.stations[dataRowIdx].stationId;
fetch(`/api/station/${stationId}`, {
method: "DELETE"
})
.then(response => response.json())
.then(data => {
if (data.error) {
this.setState({ error: data.error });
} else {
this.loadStations();
}
}).catch(error => {
this.setState({
error: error.message
});
});
}
render() {
return (
<div>
<h4>Stations</h4>
<EditableTable
columns={columns}
data={this.state.stations}
onDelete={this.deleteStation}
onChanged={this.saveStation}
></EditableTable>
</div>
);
}
}
EditableTable.js (the functional component)
import React, { useState } from "react";
import EditableLabel from "./EditableLabel";
export default function Table(props) {
var emptyDataRow = {};
props.columns.forEach( (column) => {
emptyDataRow[column.property] = ""
});
const [newRowState, setNewRowState] = useState(emptyDataRow);
function cellChanged(e, value, dataRowIdx, columnProperty) {
var dataRow = props.data[dataRowIdx];
dataRow[columnProperty] = value;
props.onChanged(e, dataRow);
}
return <table>
<thead>
<tr>
{props.columns.map( (column, idx) =>
<th key={idx} value>{column.label}</th>
)}
<th></th>
</tr>
</thead>
<tbody>
{props.data.map( (dataRow, dataRowIndex) =>
<tr key={dataRowIndex}>
{props.columns.map( (column, columnIndex) =>
<td key={columnIndex}>
<EditableLabel
value={dataRow[column.property]}
column={column}
onChanged={(e, newValue) => { cellChanged(e, newValue, dataRowIndex, column.property); }}
></EditableLabel>
</td>
)}
<td><button onClick={(e) => { props.onDelete(e, dataRowIndex); }}>delete</button></td>
</tr>
)}
</tbody>
</table>
}
This is most likely because you are using the index given by the map method as the key.
This just uses the item's location in the array as the key, which will not be a sufficient identifier if something in the middle of the array has been removed.
You should give a key that is identifiably unique for that item in the array, eg an unchanging ID or name.
I have state data in my index.js page that is being sent to my details.js page via the Link component built into gatsby. From my details.js page, I am trying to send data to my component ChartData.js.
In details.js I can access my information by using {props.location.state.x}. Now I need the same data to be sent to my component and what I did was put Stock (Stock is the class name in ChartData.js) the comonent and set 'symbol' equal to the way I would reference data just like this: {<Stock symbol={props.location.state.symbol}/>}
Now under ChartData/.js when I try to reference symbol I get the error 'symbol is not defined'. Not sure if I am messing up the syntax of passing it or if you cannot do it this way.
index.js:
import React from "react"
import { Link } from "gatsby"
import axios from "axios"
import "../css/style.css"
import Layout from "../components/layout"
import { symbol } from "prop-types"
//import Stock from "../components/ChartData"
//import Characters from "../components/ChartData"
export default class index extends React.Component {
state = {
companyName: "",
previousClose: "",
marketCap: "",
change: "",
symbol: "",
topStocks: [],
Yearweekhigh: "",
Yearweeklow: "",
avgTotalVolume: "",
peRatio: "",
}
clickHandler = (event) => {
if (event.keyCode === 13) {
const query = event.target.value;
const API_KEY = '******************';
axios.get(`https://cloud.iexapis.com/stable/stock/${query}/quote?token=${API_KEY}`)
.then(res => {
const companyName = res.data['companyName'];
this.setState({ companyName })
const previousClose = res.data['previousClose'];
this.setState({ previousClose })
const marketCap = res.data['marketCap'];
this.setState({ marketCap })
const change = res.data['change'];
this.setState({ change })
const symbol = res.data['symbol'];
this.setState({ symbol })
const Yearweekhigh = res.data['week52High'];
this.setState({ Yearweekhigh })
const Yearweeklow = res.data['week52Low'];
this.setState({ Yearweeklow })
const avgTotalVolume = res.data['avgTotalVolume'];
this.setState({ avgTotalVolume })
const peRatio = res.data['peRatio'];
this.setState({ peRatio })
const open = res.data['open'];
this.setState({ open })
const high = res.data['high'];
this.setState({ high })
const low = res.data['low'];
this.setState({ low })
const volume = res.data['volume'];
this.setState({ volume })
})
}
}
render() {
return (
<Layout>
<div class = "main-div">
<input type="search" class="main-search" onKeyDown={event => this.clickHandler(event)}/>
<table>
<tr>
<th>Ticker-Symbol</th>
<th>Market Cap</th>
<th>Previous Close</th>
</tr>
<tr>
<td>
<Link to='/details/' state={{
setState: this.state.symbol,
companyName: this.state.companyName,
previousClose: this.state.previousClose,
marketCap: this.state.marketCap,
change: this.state.change,
Yearweekhigh: this.state.Yearweekhigh,
Yearweeklow: this.state.Yearweeklow,
avgTotalVolume: this.state.avgTotalVolume,
peRatio: this.state.peRatio,
open: this.state.open,
high: this.state.high,
low: this.state.low,
volume: this.state.volume,
symbol: this.state.symbol
}}>
{this.state.symbol}</Link>
</td>
<td>{this.state.marketCap}</td>
<td>{this.state.previousClose}</td>
</tr>
</table>
</div>
<div>
{
this.state.topStocks.length && this.state.topStocks.map(stock => (
<h1>{stock.symbol}</h1>
))
}
</div>
</Layout>
)
}
}
details.js
//import { Link } from "gatsby"
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import Layout from '../components/layout';
import "../css/style.css"
import Stock from "../components/ChartData"
const Details = props => {
const [yourState, setYourState] = useState('');
useEffect(() => {
}, []);
return <Layout>
<div>
<h1 class="details-company-name">{props.location.state.companyName}</h1>
<div class = "details-div">
<div class="details-div-1">
<p>Open <h2>{props.location.state.open}</h2> </p>
<p>High <h2>{props.location.state.high}</h2> </p>
<p>Low <h2>{props.location.state.low}</h2> </p>
<p>52 WK HIGH <h2>{props.location.state.Yearweekhigh}</h2> </p>
<p>52 WK LOW <h2>{props.location.state.Yearweeklow}</h2> </p>
<p>{props.location.state.symbol}</p>
</div>
<div class="details-div-2">
<p>VOLUME <h2>{props.location.state.volume}</h2></p>
<p>AVG VOL <h2>{props.location.state.avgTotalVolume}</h2> </p>
<p>MKT CAP <h2>{props.location.state.marketCap}</h2></p>
<p>P/E RATIO <h2>{props.location.state.peRatio}</h2></p>
<p>DIV/YIELD</p>
</div>
</div>
</div>
<Stock symbol={props.location.state.symbol}/>
</Layout>;
};
export default Details;
ChartData.js
import React from 'react'
import Plot from 'react-plotly.js'
class Stock extends React.Component {
constructor(props) {
super(props);
this.state = {
stockChartXValues: [],
stockChartYValues: [],
};
}
componentDidMount() {
this.fetchStock();
}
fetchStock() {
const pointerToThis = this;
const API_KEY = '*****************';
let API_CALL = `https://cloud.iexapis.com/stable/${symbol}/aapl/chart/5y?token=${API_KEY}`;
let stockChartXValuesFunction = [];
let stockChartYValuesFunction = [];
fetch(API_CALL)
.then(function (response) {
return response.json();
})
.then(function (data) {
for (var x in data) {
stockChartXValuesFunction.push(x);
stockChartYValuesFunction.push(
data[x]['uOpen']
);
pointerToThis.setState({
stockChartXValues: stockChartXValuesFunction,
stockChartYValues: stockChartYValuesFunction,
});
}
})
}
render() {
return (
<div>
<Plot
data={[
{
x: this.state.stockChartXValues,
y: this.state.stockChartYValues,
type: "scatter",
mode: "lines+markers",
marker: {color: "red"}
},
]}
layout={{ width: 720, height: 440, title: "A Fancy Plot"}}
/>
</div>
)
}
}
export default Stock
You have a few issues there:
Everything you pass through props must be received by the child component accessing to those props. So instead of symbol, you must this.props.symbol. Since you are not destructuring your props:
let API_CALL =
`https://cloud.iexapis.com/stable/${this.props.symbol}/aapl/chart/5ytoken=${API_KEY}`;
To avoid wrong or empty calls, I would ensure that you have your props properly set and I would add a condition like:
componentDidMount() {
if(props.symbol) this.fetchStock();
}
You may need to add a constructor in your file to gather props:
constructor(props) {
super(props);
this.state = {}; // remove if not needed
}
I am building a shopping cart using MERN stack. The problem is that my images are not appearing, and on refresh the image appears only momentarily. it is showing me an icon that the image cannot be displayed. All my images are in the images folder inside src folder. Here is my code.
import React, { Component } from "react";
import "../css/styles.css";
import {
Button,
Modal,
ModalHeader,
ModalBody,
ModalFooter,
Form,
FormGroup,
Label,
Input
} from "reactstrap";
import axios from "axios";
import Cart from "./cart1.component";
export default class BookDetails extends Component {
constructor(props) {
super(props);
this.state = {
book: [],
modal: false,
cover: ""
};
}
componentDidMount() {
axios
.get("http://localhost:4000/books/" + this.props.match.params.id)
.then(response => {
this.setState({ book: response.data, cover: response.data.cover });
})
.catch(function(err) {
console.log(err);
});
}
toggle = () => {
this.setState({
modal: !this.state.modal
});
};
AddToCart = e => {
const { book } = this.state;
let id = book._id;
let cover = book.cover;
let price = book.price;
let qty = 1;
let product = { id: id, cover: cover, price: price, qty: qty };
let existing = JSON.parse(sessionStorage.getItem("cart"));
existing = existing ? existing : [];
let val = existing.filter(item => item.id === id);
if (existing.length !== 0) {
if (val.length != 0) {
existing.forEach(item => {
if (item.id === id) {
item.qty++;
}
});
} else {
existing.push(product);
}
} else {
existing.push(product);
}
sessionStorage.setItem("cart", JSON.stringify(existing));
};
getImages = cover => {
let imagesURL = [
"../images/csharp.jpg",
"../images/css.jpg",
"../images/javascript.jpg",
"../images/json.jpg",
"../images/lpthw.jpg",
"../images/mongodb.jpg",
"../images/node.jpg",
"../images/php.jpg",
"../images/python.jpg",
"../images/sql.jpg"
];
const bookUrl = `../images/${cover}`;
let coverUrl = "../images/nocover.jpg";
imagesURL.forEach(url => {
if (url == bookUrl) {
coverUrl = bookUrl;
}
});
return coverUrl;
};
render() {
const { book, quantity } = this.state;
let src = this.getImages(this.state.cover);
return (
<div className="container">
<div className="row">
<div className="col-sm-6">
<img src={src}></img>
</div>
<div className="col-sm-6">
<h2>{book.title}</h2>
<ul>
<li>Category: {book.category}</li>
<li>Author: {book.author}</li>
</ul>
<p className="button blue">${book.price}</p>
<p>{book.description}</p>
<button
id={book._id}
onClick={() => {
this.toggle();
this.AddToCart();
}}
className="btn btn-success"
>
Add to Cart
</button>
<Modal isOpen={this.state.modal} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>My Cart</ModalHeader>
<ModalBody>
<Cart></Cart>
</ModalBody>
</Modal>
</div>
</div>
</div>
);
}
}
I had been stuck on it for almost two days now. I tried using require in the src component but that is giving me an error that the ../images/nocover.jpg module cannot be found. Please, I would really appreciate some help.
I am using WebSockets to update upvotes on comments in React. I am receiving comment updates in logs of different client instances. However, React does not render the updates to upvotes.
Code I am trying:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
class Comment extends Component {
constructor(props){
super(props);
this.upvotes = React.createRef();
this.downvotes = React.createRef();
this.handleUpvote = this.handleUpvote.bind(this);
this.handleDownvote = this.handleDownvote.bind(this);
}
handleUpvote(){
console.log(this.props);
const json = { type: 'upvote' };
json.data = this.props;
json.data.comment.upvotes++;
console.log(json);
this.props.socket.send(JSON.stringify(json));
}
handleDownvote(){
this.downvotes.current.innerHTML++;
console.log(this.downvotes.current.innerHTML);
}
render() {
return (
<tr>
<td>{this.props.comment.user.firstName} {this.props.comment.user.lastName}</td>
<td>{this.props.comment.content }</td>
<td> <span ref={this.upvotes}>{this.props.comment.upvotes}</span> <button onClick={this.handleUpvote}>Upvote</button> </td>
<td> <span ref={this.downvotes}>{this.props.comment.downvotes}</span> <button onClick={this.handleDownvote}>Downvote</button> </td>
</tr>
)
}
}
export default class ListComments extends Component {
constructor(props){
super(props);
this.state = { comments: [] }
}
componentDidMount(){
axios.get('http://localhost:5000/api/comments/')
.then(resp => this.setState({ comments : resp.data }))
.catch(err => console.log(err));
}
componentWillReceiveProps(nextProps){
const data = JSON.parse(nextProps.comment);
console.log(data.data);
if(data.type === "upvote"){
// const a = this.state.comments;
// a.forEach(comment => {
// if(comment._id == data.data.comment._id){
// comment = data.data.comment
// }
// });
// this.setState({ comments : a })
this.setState(prevState => {
// Get previous state
const { comments } = prevState;
// Add new item to array
comments.forEach(comm => {
if(comm._id == data.data.comment._id){
comm = data.data.comment
}
});
// Return new state
return { comments };
});
}
else if(data.type === "comment"){
this.setState({ comments : [data.data, ...this.state.comments] })
}
}
commentList() {
return this.state.comments.map(currentcomment => {
return <Comment comment={currentcomment} socket={this.props.actions} key={currentcomment._id}/>;
})
}
render() {
return (
<div>
<h3>Comments</h3>
<table className="table">
<thead className="thead-light">
<tr>
<th>Username</th>
<th>Content</th>
<th>Upvotes</th>
<th>Downvotes</th>
</tr>
</thead>
<tbody>
{ this.commentList() }
</tbody>
</table>
</div>
);
}
}
Outputs I am getting -
Client one with 3 upvotes to question 1
Client 2 with updates to upvotes received in console, not rendred in actual comment
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
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/