Increment the ID for every addition of elements in React - arrays

i am having a problem where i cannot able to increment the ID of the state element for every addition of elements in the row. What i am getting is same number is repeating for every addition of elements, i need something like ID should be 1 to n numbers of addition.
In text-box, i am not entering the ID, i enter only the LText(Name).
In general, the ID should generate for every addition of elements.
What i have tried is..
export default class EPIM091 extends cntrl.WITBase {
constructor(props) {
super(props);
const witState = this.state;
if (Object.keys(witState).length == 0) {
witState.model = { LID: 1, LText: '', SOrder: '', Inventoried: false, Location:[] }; //Here i need to store all other state values to the Location Array
}
}
clickAction = (e) => {
if (e.id == 'btn_add') {
//var i = 1;
const { model } = this.state;
if (model.LText != null) {
if ((model.Location || []).length == 0) {
model.Location = [];
}
// this.setState(prevState => { return { LID: e.id == 'btn_add' ? prevState.LID + 1 : prevState.LID - 1 } }); //This does not worked
model.Location.push({ "LID": model.LID.toString(), "LText": model.LText, "SOrder": model.SOrder, "Inventoried": model.Inventoried.toString() });
this.setState({
model: model,
LID: model.LID + 1 //This also not worked
});
}
}
};
render(){
// Due to confusion of code, i did not add the textboxes codes
<cntrl.WITButton id="btn_add" onWitClick={this.clickAction} />
}
I need something like when i add, i should get the Unique LID from 1 to the number of elements added. What i am getting is same ID i.e 1. Thank you

Many problems here :
1) You are trying to modify a variable that you declared constant:
const witState = this.state;
if (Object.keys(witState).length == 0) {
witState.model = { LID: 1, LText: '', SOrder: '', Inventoried: false, Location:[] };
2) You are trying to access state variable, but you never define it:
const { model } = this.state;
3) You try to modify it even if it's declared constant:
if (model.LText != null) {
if ((model.Location || []).length == 0) {
model.Location = [];
}
// this.setState(prevState => { return { LID: e.id == 'btn_add' ? prevState.LID + 1 : prevState.LID - 1 } }); //This does not worked
model.Location.push({ "LID": model.LID.toString(), "LText": model.LText, "SOrder": model.SOrder, "Inventoried": model.Inventoried.toString() });
this.setState({
model: model,
LID: model.LID + 1 //This also not worked
});
}
Unless there is a lot of code you didn't show us, you must have a lot of errors in your console. Watch it.
-[Edit]-:
The problem is that I can't really help you unless I have the whole code, because as it is, it doesn't make sense. If you just want to increment your LID state var, here is a working example:
constructor(props) {
super(props);
this.state={LID:1}
}
test=(e)=>{
let {LID} = this.state;
this.setState({LID:LID+1})
console.log(this.state.LID);
}
render(){
return(
<button onClick={this.test}> Test</button>
)
}

Related

React increment and decrement array counters in state

I have some trouble trying to figure out why I can't correctly increment/decrement four counters in an array, which is in the app state. The array in the state is this:
...
constructor(props) {
super(props);
this.state = {
right: 0,
left: 0,
up: 0,
down: 0,
...more variables...
bank: [0, 0, 0, 0] // <- array with 4 counters set to zero
};
}
...
The application fetches JSON data from a Node.js server, and saves the incoming data in the "left", "right", "up" and "down" vars you can see in the state. I select which counter should be incremented/decremented (which I called 'bank') reading the "left" and "right" JSON data, and increment or decrement with "up" and "down". Those signals are working correctly, but the problem is in the logic I think.
This is the function that handles the bank selection (the counter):
bankSelection() {
if (parsedJsonData.right) {
currentBank += 1;
if (currentBank > maxBank) { // restart from zero
currentBank = 0;
}
} else if (parsedJsonData.left) {
currentBank -= 1;
if (currentBank < 0) { // restart from 3
currentBank = 3;
}
}
}
I don't save the bank value in the state, since it shouldn't be rendered. The problem comes with the increment/decrement function:
updateCounters() {
if (parsedJsonData.up) {
if (this.state.bank[currentBank] < 9) {
let bankValue = this.state.bank[currentBank] + 1;
this.setState({ bank: bankValue });
} else {
this.setState({ bank: this.state.bank[currentBank] = 0 });
}
} else if (parsedJsonData.down) {
if (this.state.bank[currentBank] > 0) {
let bankValue = this.state.bank[currentBank] - 1;
this.setState({ bank: bankValue });
} else {
this.setState({ bank: this.state.bank[currentBank] = 9 });
}
}
}
I see the React engine complains on line 7 and 14, saying I shouldn't mutate the state directly, but I'm inside a setState function!
Beside this, when I send a JSON formatted like this:
{
"left": 0,
"right": 0,
"up": 1,
"down": 0
}
the first time the counter in the first bank is updated correctly and shows value 1, but the second time I get this error in the browser's console:
Uncaught (in promise) TypeError: Cannot create property '0' on number '1'
at App.updateCounters (App.js:145:1)
at App.js:112:1
I tryed so many solutions I'm going crazy, any help would me appreciated...
You can find the full code here but bear in mind it's still a work in progress in the render method (the first bank is the only one usable, still testing various solutions).
I hope I gave you all the info to help me fix the error.
I see a few problems with your code.
bank's value is array. You're trying to change it to integer with this.setState({ bank: bankValue })
State should be immutable, if you wish to change the value, you need to create a new array and set the bank state. Something like this:
this.setState({bank: Object.values({...this.state.bank, 1: 100}); // where 1 is the idx where you want to update the value, and 100 is the new value
Finally, I managed to solve the problem.
I managed this using the spread operator and avoiding direct assignment inside the setState method.
updateCounters() {
if (parsedJsonData.up) {
if (this.state.bank[currentBank] < 9) {
// let bankValue = this.state.bank[currentBank] + 1;
var newArrayUp = [...this.state.bank];
newArrayUp[currentBank] += 1;
console.log("array clonato dopo aggiornamento " + [...newArrayUp]);
this.setState({ bank: [...newArrayUp] }, () => {
this.forceUpdate();
});
} else {
var zeroArray = [...this.state.bank];
zeroArray[currentBank] = 0;
console.log(zeroArray[currentBank]);
this.setState({ bank: [...zeroArray] });
this.forceUpdate();
}
} else if (parsedJsonData.down) {
if (this.state.bank[currentBank] > 0) {
var newArrayDown = [...this.state.bank];
newArrayDown[currentBank] -= 1;
console.log("array clonato dopo aggiornamento " + [...newArrayDown]);
this.setState({ bank: [...newArrayDown] }, () => this.forceUpdate());
} else {
var nineArray = [...this.state.bank];
nineArray[currentBank] = 9;
console.log(nineArray[currentBank]);
this.setState({ bank: [...nineArray] }, () => this.forceUpdate());
}
}
}
Now I create a copy of the original array, work on it and then pass it to the setState method.
Thanks for suggestions!

Adding and removing tracks from a Spotify playlist app

I'm currently completing a project where I have to build a Spotify playlist creator. For part of this, I need to add or remove tracks from the playlist. I've coded a method that appears to work, but it is different from the official solution, so I just want to see whether there is a reason I shouldn't do it my way.
Specifically, they use .find and .filter methods where I have used .includes. Is there a downside to what I've done?
Their code
addTrack(track) {
let tracks = this.state.playlistTracks;
if (tracks.find(savedTrack => savedTrack.id === track.id)) {
return;
}
tracks.push(track);
this.setState({ playlistTracks: tracks});
}
removeTrack(track) {
let tracks = this.state.playlistTracks;
tracks = tracks.filter(currentTrack => currentTrack.id !== track.id);
this.setState({playlistTracks: tracks});
}
My code
addTrack(track) {
let tracks = this.state.playlistTracks;
if (!tracks.includes(track)) {
tracks.push(track);
}
this.setState({playlistTracks: tracks});
}
removeTrack(track) {
let tracks = this.state.playlistTracks;
if (tracks.includes(track)) {
let index = tracks.indexOf(track);
tracks.splice(index, 1);
}
this.setState({playlistTracks: tracks});
}
Yes, there is a significant difference, because includes() will only return true if you pass it the actual instance (by that I mean a reference that points to the same object) of track that you are looking for.
The provided solution compares tracks only based on the track ID, so that is something different.
See the following example:
const tracks = [
{id: 1, title: "mysong1"},
{id: 2, title: "mysong2"},
]
function isTrackPresentIncludes(track){
return tracks.includes(track);
}
function isTrackPresentFind(track){
return tracks.find(it => it.id === track.id) !== undefined;
}
// this will be true
console.log("with includes(tracks[0]):\n", isTrackPresentIncludes(tracks[0]))
// this will be false as it's a different object
console.log("with includes({id: 1, title: \"mysong1\"}):\n", isTrackPresentIncludes({id: 1, title: "mysong1"}))
// this will be true
console.log("with find(tracks[0]):\n", isTrackPresentFind(tracks[0]))
// this will also be true
console.log("with find({id: 1, title: \"mysong1\"}):\n", isTrackPresentFind({id: 1, title: "mysong1"}))
You have the same issue with indexOf() in your removeTrack().
There is another thing I don't particularly like about the solution. find() returns the track that was found but that return value is never actually used so to my mind you should use some() instead which just returns true or false.
I don't think this is a problem here but it could potentially lead to unexpected behavior if an array would hold falsy values.
Consider this:
const arrayWithFalsyValues = [
0, // zero is falsy!
1,
2,
3,
4,
5,
6,
7,
8
]
function isPresent(toBeFound){
if(arrayWithFalsyValues.find(number => number === toBeFound)){
console.log(`Value ${toBeFound} found in array`);
}
else{
console.log(`Value ${toBeFound} NOT found in array`);
}
}
console.log("Array:", arrayWithFalsyValues)
// this will work as expected
console.log("Contains 3?")
isPresent(3)
console.log("Contains 8?")
isPresent(8)
console.log("Contains 10?")
isPresent(10)
// now search for the falsy value -> incorrect result
console.log("Contains 0?")
isPresent(0)
Issue is with referencing, You have make another reference of playlistTracks
addTrack(track) {
let { playlistTracks } = this.state;
let tracks = [...playlistTracks];
if (!tracks.includes(track)) {
tracks.push(track);
}
this.setState({ playlistTracks: tracks });
}
removeTrack(track) {
let { playlistTracks } = this.state;
let tracks = [...playlistTracks];
if (tracks.includes(track)) {
let index = tracks.indexOf(track);
tracks.splice(index, 1);
}
this.setState({ playlistTracks: tracks });
}
MY SUGGESTION
addTrack(track) {
const { playlistTracks } = this.state;
const tracks = [...playlistTracks];
const index = tracks.indexOf(track);
if (index < 0) {
tracks.push(track);
}
this.setState({ playlistTracks: tracks });
}
removeTrack(track) {
const { playlistTracks } = this.state;
const tracks = [...playlistTracks];
const index = tracks.indexOf(track);
if (index > -1) {
tracks.splice(index, 1);
}
this.setState({ playlistTracks: tracks });
}

Single row value change is reflected in all the drawers

I have two drawer components and inside those drawer user can select maximum 3 rows. Each row has a Accessory Qty property which cannot be greater than Max Qty.
Two Drawers, each with clickHandler which triggers inner row sections
Each row has Accessory Qty and Max Qty. as mentioned above.
Selecting all the rows and clicking Associate to all which adds the selected rows in the other drawers. You can see in the breadcrumbs that currently I'm in SET F. Now after going back to Image one, when I click the 2nd drawer which is SET H I can see that the selected Items are added here.
Now if I change any value in any of the rows of SET H the same change is reflected in the SET F
This is the whole problem, I want the changes not to reflect in other drawers.
For this data is getting fetched from the API. I'm setting a copy of the data in the backupdata. Then with the index and values I'm changing the values in the corresponding rows.
handleQtyChange
const handleQtyChange = (event, i) => {
console.log("Set Index QTY", index, i);
const backupdata = [...billOfMaterials];
var data = event.target.value;
if (backupdata[index].accessory_details[i].accessory[0].code !== "") {
var accesory_code =
backupdata[index].accessory_details[i].accessory[0].code;
var max_qty = backupdata[index].accessory_details[i].max_qty;
var updated_qty = event.target.value;
if (parseFloat(updated_qty) > parseFloat(max_qty)) {
setMessageDialog({
isOpen: true,
message: "Qty must be less than or equal to Max qty",
severity: "error",
});
} else {
if (data == "" || (!isNaN(+data) && parseFloat(data) >= 0)) {
console.log("AccessoryChildIndex", i);
console.log("Max qty", accessoryListWithMaxQty);
console.log("Set Index", index);
setMessageDialog({
isOpen: false,
message: "",
severity: "error",
});
var accessory_total_qty_index = backupdata[
index
].accessory_total_qty.findIndex(
(x) =>
x.code == backupdata[index].accessory_details[i].accessory[0].code
);
console.log("accessory_total_qty Index", accessory_total_qty_index);
backupdata[index].accessory_total_qty[accessory_total_qty_index].qty =
updated_qty;
backupdata[index].accessory_details[i].accessory_qty = updated_qty;
backupdata[index].accessory_details[i].total = (
parseFloat(
event.target.value == undefined || event.target.value == ""
? 0
: event.target.value
) * parseFloat(backupdata[index].accessory_details[i].rate)
).toFixed(2);
backupdata[index].grand_total = grandTotal();
setBillOfMaterials(backupdata);
if (!checkBomAccessoryWiseTotalQty(accesory_code, max_qty)) {
setMessageDialog({
isOpen: true,
message: "Qty limit exceed max qty would be " + max_qty,
severity: "error",
});
backupdata[index].accessory_total_qty[
accessory_total_qty_index
].qty = 0;
backupdata[index].accessory_details[i].accessory_qty = 0;
backupdata[index].accessory_details[i].total = 0;
backupdata[index].grand_total = grandTotal();
setBillOfMaterials(backupdata);
}
console.log("Change BOM", billOfMaterials);
}
}
}
};
handleAssociateToAll
const handleAssociateToAll = () => {
//var data = [...billOfMaterials];
var backupdata = [...billOfMaterials];
//var backupdata = JSON.parse(JSON.stringify([...billOfMaterials]));
var selectedData = backupdata[index].accessory_details.filter((el) => {
return el.is_selected == true;
});
console.log(selectedData);
console.log(index);
backupdata.map((el, i) => {
if (index != i) {
console.log(selectedData);
selectedData.map((dt) => {
dt.accessory_qty = "0";
dt.is_selected = false;
let checkAccessory = backupdata[i].accessory_details.find((data) => {
return (
data.accessory[0].code != "" &&
data.accessory[0].code == dt.accessory[0].code
);
});
console.log("Check", checkAccessory);
if (checkAccessory == undefined) {
backupdata[i].accessory_details = [
dt,
...backupdata[i].accessory_details,
];
}
});
backupdata[index].accessory_total_qty &&
backupdata[index].accessory_total_qty.map((qty) => {
qty.qty = "0";
let checkTotalQty = backupdata[i].accessory_total_qty.find(
(data) => {
return data.code == qty.code;
}
);
if (checkTotalQty == undefined) {
backupdata[i].accessory_total_qty = [
...backupdata[i].accessory_total_qty,
qty,
];
}
});
}
});
setBillOfMaterials(backupdata);
checkAllSelected();
};
console.log("bill", billOfMaterials);
I guess I've explained well. So how do I keep the other drawer's data unchanged and change only the selected drawers rows.
The api response
After clicking Associate to all:
The overall bill, the selected rows are added in accessory_total_qty:

How to write array with deleted image id?

When i remove images from news i catch id, and id come to along.
How to write in array all this lonlies id ?
How to create streamIds array with streamId ?
this.state = {
mainImage: null,
mainImageUrl: "",
crop: {
aspect: 2 / 1
},
pixelCrop: null,
cropped: false,
loaded: false,
streamIds: []
};
removeImage(imageKey, streamId) {
const {singleNews} = this.props;
let streamIds = this.state.streamIds;
console.log(streamId);
singleNews.secondaryImages.splice(imageKey, 1);
if (!singleNews.secondaryImages.length) {
singleNews.secondaryImages = null;
delete singleNews.secondaryImages;
this.props.updateSingleNews(null, singleNews);
} else {
streamIds.push(streamId);
singleNews.secondaryImages.map(image => {
const index = singleNews.secondaryImages.indexOf(image);
if (index > -1) {
singleNews.secondaryImages.slice(index, 1);
FilesApi.getDocument(image.streamId).then(resp => {
singleNews.secondaryImages[index] = new File([resp], image.name, {lastModified: Date.now()});
});
}
});
this.props.updateSingleNews('streamIds', streamIds);
}
}
this is your method
If not in this func where i need to place
if you want to keep the array of ids in the same component, use
let streamIds = [];
at the top of your react component and do
removeImage (imageKey, streamId) {
console.log(streamId);
streamIds.push(streamId); // insert the item to array
}
in your removeImage method
if you want to keep the removed ids in the application state, then the concept is the same, but it need to be done on the state management tool you are using (like redux, mobx etc)

ReactJS sorting column with dynamic class creation

I've inherited a large pile of code and it generally works. This is running on v0.13.3.
The problem I am having is in sorting columns. Whenever I invoke the handler for the click event below, I am getting a null this.state. I must be missing something simple because I have done a lot of stare/compare vs other working code and I just don't see it but this example component is hassling me.
I've simplified down to just the problem component as a standalone html file with static sample data:
<div id="content">Loading ...</div>
<script>
var theData = [{"id":"47483648","labName":"Lab0"},{"id":"47483650","labName":"Lab1"},{"id":"47483651","labName":"Lab2"},{"id":"47483654","labName":"Lab3"}];
function render() {
React.render(React.createElement(aTable, {data: theData}),document.getElementById("content"));
}
var aTable = React.createClass({displayName: "aTable",
handleHeaderClick: function(sortBy) {
console.log("firing aTable handleHeaderClick");
//this.state is null here
var newState = this.state;
if (this.state.sortBy === sortBy && this.state.sortOrder === 'asc') {
newState.sortOrder = 'desc';
}
else {
newState.sortOrder ='asc';
}
newState.sortBy = sortBy;
this.setState(newState);
},
render: function(){
var theItems = $.map(this.props.data, function (key, value) {
console.log("the items", key);
return (
React.createElement("tr",{key: key.id + "-row"},
React.createElement("td", {key: key.agentid}, key.id),
React.createElement("td", {key: key.labName}, key.labName),
)
);
});
return (
React.createElement("div", {id: "holder"},
React.createElement("div", {id: "a-table"},
React.createElement("table",null,
React.createElement("thead",null,
React.createElement("tr",null,
React.createElement("th",null," The ID"),
React.createElement("th",
{onClick: this.handleHeaderClick.bind(this, "labName")},
"Lab Name")
)
),
React.createElement("tbody",null,
theItems
)
)
)
)
);
}
});
$(document).ready(function () {
render();
});
This throws up an exception on the null state:
basic.html:37 Uncaught TypeError: Cannot read property 'sortBy' of null
Which refers to this line:
if (this.state.sortBy === sortBy && this.state.sortOrder === 'asc') {
I have latitude to change the handler logic, but am stuck with this pattern for the time being.
Thanks in advance.
EDIT 1
This is a working fiddle where I worked out the sorting by doing a field sort on the data array after Sy pointed out where I was missing the initial state: working fiddle
state is null because you didn't set an initial value.
Test = React.createClass({
getInitialState: function() {
return {};
}
})
this is equivalent to
class Test extends Component {
constructor(props) {
this.state = {}
}
}

Resources