Trouble with array and state - javascript

Can someone explain, why I'm getting an error, when I'm removing items from an array? It works once, but then it crashes. Checked - is boolean meaning.
removeCards = () => {
console.clear();
for (let i = 0; i < this.state.cards.length; i++) {
console.log(this.state.cards[i]);
if (this.state.cards[i].checked) {
delete this.state.cards[i];
}
}
this.setState({ cards: this.state.cards });
};

In React, state is immutable. So instead of trying to alter it directly, create a copy of it and then apply that to state -
removeCards = () => {
console.clear();
const newCards = [];
for (let i = 0; i < this.state.cards; i++) {
if (!this.state.cards[i].checked) {
newCards.push(this.state.cards[i];
}
}
this.setState({ cards: newCards });
};

Maybe my trouble in that? That's how i'm chaning states from true to false and in reverse.
myFunc = (props) => {
let num = Number(props);
num--;
let cards = [...this.state.cards];
if (this.state.cards[num].checked) {
cards[num] = { ...cards[num], checked: false };
} else {
cards[num] = { ...cards[num], checked: true };
}
this.setState({ cards });
};

Related

Shopware 6 : Delete data with custom Administration

I want remove Data of table 'filter_object' with related table 'filter_link' bevor starting the action of button onClickSync().
The 'filter_link' table contains two foreign key : product_id and object_id.
I tried to delete the data by id in a for loop, but this only deletes the data from the 'filter_object' table without related product. In addition it slows down the deletion when I have several data. Could help me please ?
import template from './sw-vehicles-list.html.twig';
const { Component } = Shopware;
const { Criteria } = Shopware.Data;
Component.register('sw-vehicles-list', {
template,
inject: ['repositoryFactory'],
data() {
return {
repository: null,
showAddButton: true,
isLoading: false,
object: null,
};
},
metaInfo() {
return {
title: this.$createTitle()
};
},
computed: {
filterObjectRepository() {
return this.repositoryFactory.create('filter_object');
},
filterLinkRepository() {
return this.repositoryFactory.create('filter_link');
},
productRepository() {
return this.repositoryFactory.create('product');
},
},
created() {
this.object = this.repositoryFactory.create('filter_object');
this.link = this.repositoryFactory.create('filter_link');
},
methods: {
async onClickSync() {
this.isLoading = true;
await this.removeData();
this.repository.search(new Criteria(), Shopware.Context.api).then((result) => {
if (result.length) {
var i;
var manufacturer = [];
for (i = 0; i < result.length; i++) {
manufacturer.push(result[i]['manufacturer']);
}
var manufacturerFilter = Array.from(new Set(manufacturer));
var j;
for ( j = 0; j < manufacturerFilter.length; j++) {
// some code
}
}
});
},
removeData() {
return this.filterObjectRepository.search(new Criteria(), Shopware.Context.api).then((result) => {
if (result.length) {
var i;
for (i = 0; i < result.length; ++i) {
this.filterObjectRepository.delete(result[i]['id'], Shopware.Context.api).then(this.loadObject);
}
return null;
}
});
},
loadObject() {
this.filterObjectRepository.search(new Criteria(), Shopware.Context.api).then((result) => {
this.result = result;
});
},
}
});
You can use the syncDeleted method of the repository to delete multiple records.
const ids = result.map(record => record.id);
this.filterObjectRepository.syncDeleted(ids, Shopware.Context.api)
If you want filter_link records to be deleted when deleting records from filter_object you'll have to set the foreign keys ON DELETE subclause to CASCADE.
ALTER TABLE `filter_link`
DROP FOREIGN KEY `fk.filter_link.object_id`;
ALTER TABLE `filter_link`
ADD CONSTRAINT `fk.filter_link.object_id` FOREIGN KEY (`object_id`) REFERENCES `filter_object` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

React js - setState and array of objects

I'm new to React, so I'm struggling with this:
why does setting state as a single object array works, but getting the array from state, pushing to it, and setting as state again doesn't work?
This is the working code. I have this IMAGE array in as state, not initialized:
class PersonalBananas extends React.Component {
constructor(){
super();
this.state = {
username: 0,
images: 0,
pred: 0,
IMAGES: 0
}
this.imgList = this.imgList.bind(this)
}
In componentDidMount I call the imgList():
componentDidMount() {
let $this = this;
axios.get('http://localhost:8081/auth/username')
.then((response) => {
let uname = response.data;
this.setState({
username: uname
});
})
axios.get('http://localhost:8081/auth/files')
.then((response) => {
let imgs = response.data;
console.log("images response: "+imgs);
this.setState({
images: imgs
},
function() { $this.imgList() }
);
})
}
Inside imgList() I call getImgPred:
imgList = () => {
var $this = this;
const IMAGES = [];
const imgpaths = this.state.images;
console.log("images from imgLis():"+imgpaths);
for (let i = 0; i < imgpaths.length; i++) {
var path = imgpaths[i]
this.getImgPred(path);
console.log("pred:"+$this.state.pred);
}
console.log("IMAGES from imgLis():"+IMAGES);
};
And here finally I call the IMAGESpush():
getImgPred = (path) => {
var username = this.state.username;
var $this = this;
let regex = new RegExp(username+'\/(.*?)$');
let imgRegex= /{username}\/(.*?)$/;
let filename = regex.exec(path);
console.log("filename at front:"+filename[1]);
console.log("regex1:"+imgRegex+", regex2:"+regex);
axios.post('http://localhost:8081/auth/imgpred',
"filename=" + filename[1]
).then(function (response) {
console.log("response at front (get img prediction):"+response.data);
if (response.status === 200) {
$this.setState({
pred: response.data
}, function() { $this.IMAGESpush(path) } );
}
});
}
Here is the problem: when I just initialize the const IMAGES = [], push to it, then set state - it works fine. What i'm trying to do is: const Images = this.state.IMAGES. I can't do it.
IMAGESpush = (path) => {
var $this = this;
const IMAGES = [];
IMAGES.push({
src: process.env.PUBLIC_URL +`/${path}`,
thumbnail: process.env.PUBLIC_URL +`/${path}`,
thumbnailWidth: 320,
thumbnailHeight: 320,
caption: $this.state.pred
})
this.setState({
IMAGES: IMAGES
})
}
render() {
return (
<div>
<Gallery images={this.state.IMAGES}/>
</div>
)
}
}
export default PersonalBananas;
Pushing to an array does not change its reference, it's essentially the same object to React's reconciliation algorithm (which doesn't deeply compare objects inside of its state).
What you should really do is creating a new array, expanding the old one into it and adding a new element on top of that. Like this:
this.setState({
IMAGES: [...this.state.IMAGES, { /* new object here */ }]
});
You can also use the concat function to merge arrays, since it doesn't change any array - it returns a new array instead.
this.setState({
IMAGES: this.state.IMAGES.concat([{ /* new object here */ }])
});

setState() in Reactjs updating multiple properties instead of one

When I update 'classProficienciesChoices' in my state using setState() it is updating not only that property, but also where I derived the 'classProficienciesChoices' info from in the 'classSelected' property, AND ALSO from where I derived the classSelected info from in the 'classesInfo' property.
The same function I update 'classProficienciesChoices' I also update 'classProficiencies', and it updates properly in the one property I tell it to, and not the elements where the information was derived from.
Any insight would be helpful. The Create component has other components nested and none of them have state and only use props passed. There are navigation, selection, and information display components nested.
import React, { Component } from 'react'
import Create from './Create'
class App extends Component {
constructor(props) {
super(props);
const url = 'http://www.dnd5eapi.co/api/';
fetch(url + 'classes')
.then(result => result.json())
.then(result => { this.setState({ classes: result, }, this.getInfo(result)) });
}
state = {
classes: {}, //assigned value from API call in constructor
classesInfo: [], //assigned value from API call in getInfo()
classSelected: {}, //assigned a value derived from classInfo in displayClassInfo()
classProficiencies: [], //assigned a value derived from classSelected in setStartingProficiencies()
classProficienciesChoices: [], //assigned a value derived from classSelected in setStartingProficiencies()
}
getInfo(data) {
let info = []
const url = 'http://www.dnd5eapi.co'
for (var i = 0; i < data.results.length; i++) {
fetch(url + data.results[i].url)
.then(result => result.json())
.then(result => info.push(result))
}
this.setState({ classesInfo: info, })
}
}
setStartingProficiencies(chosenClass) {
const profs = chosenClass.proficiencies.map((prof) => {
return prof;
});
const proChoice = chosenClass.proficiency_choices.map((choices) => {
return choices;
});
this.setState({ classProficiencies: profs, classProficienciesChoices: proChoice, });
}
addProficiency = (proficiencyName) => {
const { classProficienciesChoices } = this.state
// classProficienciesChoices: [
// { choose: 2, type: 'proficiencies', from: [{ name: 'someName', url: 'someUrl' }, { name: 'someName', url: 'someUrl' }] },
// ]
// different classes have more objects in the parent array
let newChoiceArray = classProficienciesChoices.map((choices) => {
return choices
})
for (var i = 0; i < newChoiceArray.length; i++) {
for (var j = 0; j < newChoiceArray[i].from.length; j++) {
if (newChoiceArray[i].from[j].name === proficiencyName) {
let newChoices = newChoiceArray[i].from.filter(function (proficiency) { return proficiency.name !== pIndex })
let newProficiency = newChoiceArray[i].from.filter(function (proficiency) { return proficiency.name === pIndex })
newChoiceArray[i].from = newChoices //I think this is the problem
this.setState(state => ({
classProficiencies: [...state.classProficiencies, newProficiency[0]],
proficienciesChoices: newChoiceArray,
}))
}
}
}
}
displayClassInfo = index => {
const { classesInfo } = this.state
for (let i = 0; i < classesInfo.length; i++) {
if (classesInfo[i].index === index) {
const classSelected = classesInfo.filter(function (cClass) { return cClass.name === classesInfo[i].name })
this.setState({ classSelected: classSelected[0], isClassSelected: true }, this.setStartingProficiencies(classSelected[0]),)
break;
}
}
}
render() {
const { classes, classesInfo, classSelected, isClassSelected, classProficiencies, classProficienciesChoices } = this.state
return (<Create classes={classes} classesInfo={classesInfo} displayClassInfo={this.displayClassInfo} classSelected={classSelected} isClassSelected={isClassSelected} category='classes' classProficiencies={classProficiencies} classProficienciesChoices={classProficienciesChoices} addProficiency={this.addProficiency} />);
}
}
export default App
You call setState four times, of course it will update the state multiple times.
SOLVED!!! Stumbled across turning the array into a string and then back. It breaks the reference and creates an entirely new array. Reference type got me.
setStartingProficiencies(chosenClass) {
const proficiencies = JSON.parse(JSON.stringify(chosenClass.proficiencies))
const proficienciesChoices = JSON.parse(JSON.stringify(chosenClass.proficiency_choices))
this.setState({ classProficiencies: proficiencies, classProficienciesChoices: proficienciesChoices, });
}

How can I set the default option of dropdown while using choices library?

I am using Choices library for dropdown. What I need to do is to retain the selected value of dropdown and show it as soon as the page reloads before submitting. Right now I am able to store the value using sessionStorage. But how can I show it in the choices dropdown as the default option once the page reloads? I read the documentation but not able to figure out how to pass the default value.
document.querySelectorAll('.myDropdown').forEach(selectBox => {
choicesElements = new Choices(selectBox, { addItemText: ['Yes'], sortFn: (a, b) => a < b } );
selectBox.addEventListener('change', () => {
// code to populate choices
}
}
let marks_dropdown = document.querySelector('.myDropdown');
marks_dropdown_id.addEventListener("change",function() {
var choices_item_selectable = document.querySelector('.choices__item.choices__item--selectable')
storeSelectedItem(choices_item_selectable.innerText);
}
function storeSelectedItem(innertext) {
sessionStorage.setItem('innertext', innertext);
}
let innertext = sessionStorage.getItem('innertext');
if (innertext) {
let new_choices_item_selectable = document.querySelector('.choices__item.choices__item--selectable');
new_choices_item_selectable.innerText = innertext;
}
I solved the issue as below.
assignChoicesElements = () => {
document.querySelectorAll('.myDropdown').forEach(selectBox => {
choicesElements['some-key'] = new Choices(selectBox, { sortFn: (a, b) => a < b });
selectBox.addEventListener('change', () => {
let reqdValue = selectBox.value;
if(reqdValue != '') {
storeSelectedItem(reqdValue);
}
});
let elementsObject = {};
document.querySelectorAll('unMarked').forEach(unmarkedElement => {
elementsObject['some_key'] = //whatever value to be stored;
});
choicesElements['some-key'].clearStore();
choicesElements['some-key'].setChoices(getPossibleCombinations(elementsObject), 'value', 'label', false);
};
getPossibleCombinations = (jsonObject) => {
var possibleCombinations = {}
Object.entries(jsonObject).forEach(([key, value]) => {
var newPossibleCombinations = possibleCombinations
Object.entries(possibleCombinations).forEach(([key, value]) => {
let newValue = value
newPossibleCombinations['some-value'] = newValue
})
newPossibleCombinations['key'] = ['some-value']
possibleCombinations = newPossibleCombinations
})
var formatedPossibleCombinations = []
Object.entries(possibleCombinations).forEach(([key, value]) => {
// Here is the change. Get the stored value and while creating the list of values, add selected: true to the value if it is found in sessionStorage.
let sessionStorageValue = sessionStorage.getItem('stored_item')
if (sessionStorageValue) {
formatedPossibleCombinations.push({ label: key, value: value, selected: true })
}
})
return formatedPossibleCombinations
}
function storeSelectedItem(value) {
sessionStorage.clear();
sessionStorage.setItem('stored_item', value);
}
This code is more than required for the question. But I have added it just in case if anyone finds it useful.

TypeError: Cannot read property 'style' of undefined

export class EstimateForm extends React.Component<IEstimateFormProps,
IEstimateFormState> {
state: IEstimateFormState = {
cellUpdateCss: 'red',
toRow: null,
fromRow: null,
estimateList: null,
estimateItemList: [],
poseList: null,
levelList: null,
partList: null,
selectedEstimate: null,
totalEstimateItems: 0,
selectedIndexes: [],
totalEstimateAmount: 0,
grid: null,
projectId: 0,
};
constructor(props, context) {
super(props, context);
this.state.estimateList = this.props.estimateList;
}
rowGetter = i => {
const row = this.state.estimateItemList[i];
const selectRevison = this.state.selectedEstimate.revision;
if (row['pose.poseName']) {
const poseCode =
row['pose.poseName'].substring(row['pose.poseName'].lastIndexOf('[') + 1,
row['pose.poseName'].lastIndexOf(']'));
for (const pose of this.state.poseList) {
if (pose.poseCode === poseCode) {
row.pose = pose;
}
}
}
if (row['level.levelName']) {
const levelCode = row['level.levelName'].substring(
row['level.levelName'].lastIndexOf('[') + 1,
row['level.levelName'].lastIndexOf(']')
);
for (const level of this.state.levelList) {
if (level.levelCode === levelCode) {
row.level = level;
}
}
}
if (row['level.part.partName']) {
const partCode = row['level.part.partName'].substring(
row['level.part.partName'].lastIndexOf('[') + 1,
row['level.part.partName'].lastIndexOf(']')
);
for (const part of this.state.partList) {
if (part.partCode === partCode) {
row.part = part;
}
}
}
row.get = key => eval('row.' + key);
row.totalCost = (row.materialCost + row.laborCost) * row.amount;
const changeColor = {
backgroundcolor: 'red'
};
const all = document.getElementsByClassName('react-grid-Row') as
HTMLCollectionOf<HTMLElement>;
debugger; if (row.revision > selectRevison) {
for (let i = this.state.fromRow; i <= this.state.toRow; i++) {
all[i].style.color = 'red'; //HERE
}
return row;
}
}
handleGridRowsUpdated = ({ fromRow, toRow, updated }) => {
const rows = this.state.estimateItemList.slice();
for (let i = fromRow; i <= toRow; i++) {
const rowToUpdate = rows[i];
const updatedRow = update(rowToUpdate, { $merge: updated });
rows[i] = updatedRow;
}
this.setState({ estimateItemList: rows, fromRow: (fromRow), toRow: (toRow)
}, () => {
});
};
saveEstimateItems = () => {
if (this.state.selectedEstimate == null) {
toast.warn(<Translate
contentKey="bpmApp.estimateForm.pleaseSelectEstimate">Please select an
estimate</Translate>);
return;
}
render() {
return ()
}
I wanna to change the row color when the condition row.revision > this.state.selectedEstimate.revision . How can I prevent the change of this.color. However TypeError: Cannot read property 'style' of undefined get error but row color is not change. how can i solve this problem it is my first project in react and i dont know where is the problemThanks for your feedback guys.
Okay, so without the rest of the context because your pasted code is difficult to read and understand, the simplest reason for your issue is in this chunk:
const all = document.getElementsByClassName('react-grid-Row') as
HTMLCollectionOf<HTMLElement>;
debugger; if (row.revision > selectRevison) {
for (let i = this.state.fromRow; i <= this.state.toRow; i++) {
all[i].style.color = 'red'; //HERE
}
Essentially there's multiple things that could go wrong here, but most likely there are either no rows with that class on the page, or less than your this.state.fromRow, I see you've got the debugger in there, but you are missing a few things:
You aren't doing a null check on all to make sure you are finding something
You aren't checking whether all.length > this.state.fromRow
You aren't breaking the for loop if all.length < this.state.toRow
It's failing because all[i] doesn't exist, or there's no values:
all = [0, 1]
and you are looking for all[3] for example
Throw in those fallbacks and check what all is on page load and you should be able to figure it out.

Categories

Resources