I have a handler which is meant to update the 'quantity' property of my 'selected' array state. I have multiple fields for different items of which the quantities have to be updated by the user.When I am trying to update the state though, I get that maximum depth has been reached. When I use console.log, I get over 1000 calls of the handler somehow.. Maybe someone could guide me in what I am doing wrong. Here is the code:
{
this.state.purchase.selected.map(item => {
return (
<Grid item xs={4}>
<OrderItemsCard
item={item}
onChange={this.handleSelectedItemChange(item)} />
</Grid>
)
})
}
this.state = {
purchase: {
selected: [],
comments: ''
},
}
this.state.purchase.selected = []
for (let i = 0; i <= data.length - 1; i++) {
for (let j = 0; j <= items.length - 1; j++) {
if (this.props.data[i] === this.props.items[j]._id) {
this.state.purchase.selected.push(this.props.items[j])
}
}
}
handleSelectedItemChange = (item) => {
let selected = Object.assign([], this.state.purchase.selected)
selected.forEach(selectedItem => {
selectedItem.quantity = item.quantity
})
this.setState({selected})
}
Instead of:
onChange={this.handleSelectedItemChange(item)}
which directly calls the handler and passes its result as the event handler, you want to pass a function instead:
onChange={() => this.handleSelectedItemChange(item)}
And instead of shallow copying:
let selected = Object.assign([], this.state.purchase.selected)
you want to deep copy there:
let selected = this.state.purchase.selected.map(selected => {...selected});
Related
I try to insert a string into a particular index of object if condition is true inside a forloop but its not inserting of some reason. I tried to use push and append and splice but splice just inserting entire string as an new object into the array and i need it to just append to existing object. Any ideas how to make it work?
Data looks like that:
const [concerts, setConcerts] = useState([]);
const [tickets, setTickets] = useState([]);
const [limit, setLimit] = useState(25);
const navigate = useNavigate();
const [button, setButton] = useState(false);
const [array, setArray] = useState([]);
//Raw JSON Date example: "2023-02-08T23:15:30.000Z"
let currentDate = new Date().toJSON().slice(0, 10);
const json = { available: "true" };
useEffect(() => {
const loadConcerts = async () => {
const resConcerts = await axios.get("/data/concerts");
const resTickets = await axios.get("/data/tickets");
let table = [];
setTickets(resTickets.data);
// getting all concerts above today
const filteredData = resConcerts.data.filter((concert) => {
return concert.datum >= currentDate;
});
filteredData.forEach((element) => {
table.push(element);
// table.splice(10, 0, { status: "available" });
});
setArray(table);
for (let i = 0; i < resTickets.data.length; i++) {
for (let j = 0; j < filteredData.length; j++) {
if (
resTickets.data[i].concertid == filteredData[j].id &&
resTickets.data[i].booked == 0
) {
table.push({ status: "avaiable" });
// table.splice(10, 0, { status: "available" });
}
}
}
setArray(table);
// filteredData.forEach((concert) => {
// for (const ticket of tickets) {
// if (concert.id == ticket.concertid && ticket.booked == 0) {
// table.push(json);
// }
// }
// });
setConcerts(
filteredData.sort((a, b) =>
a.datum > b.datum ? 1 : a.datum < b.datum ? -1 : 0
)
);
};
console.log("from use effect: " + array.length);
loadConcerts();
}, []);
After using splice method:
Update
Problem is solved. I used Object.assign() helped to append string to existing object in array. Actually i had to insert another object, not a single variable.
The problem is you are trying to push a string "available" into an array-of-objects.
Here you see the object with a property datum:
const filteredData = resConcerts.data.filter((concert) => {
return concert.datum >= currentDate;
});
Yet below when you push, you are not pushing an object into the array which is problematic. It should probably be something like this but you have to verify:
Instead of this:
filteredData.push("available");
Domething like this:
filteredData.push({ datum: '', status: 'available' );
I don't know what your data object is but it's an object not a string you need to add to that array.
The looping twice is likely from React 18 New Strict Mode Behaviors. It intentionally unmounts/remounts components to fire your useEffect calls twice - so that you can identify problematic side effects. If you remove <StrictMode> or run in production that double-looping should not occur.
Problem solved. Push() neither splice() method didn't helped. What helped me to append my object to another object without changing the data was Object.assign() function
for (let i = 0; i < resTickets.data.length; i++) {
for (let j = 0; j < filteredData.length; j++) {
if (
resTickets.data[i].concertid == filteredData[j].id &&
resTickets.data[i].booked == 0
) {
Object.assign(filteredData[j], obj);
}
}
}
I was working on a chart where i need my data to be sliced into small object based for better visibility. My array that i have is
{
"size":[
{
"timestamp":"1641329889000",
"size":12345,
"fees":123456,
"cost":168
},
{
"timestamp":"1641387032",
"size":456789,
"fees":4567891,
"cost":249
},
{
"timestamp":"1641435786",
"size":98765,
"fees":987654,
"cost":987
},
{
"timestamp":"1641435786",
"size":98765,
"fees":987654,
"cost":987
},
{
"timestamp":"1641435786",
"size":98765,
"fees":987654,
"cost":987
}
]
}
in which i want the array to be in this form
{
"size":{
"timestamp": ["1641329889000","1641387032","1641435786"],
"size": [12345,456789,98765],
"fees": [123456,4567891,987654],
"cost": [168,249,987]
}
}
i can achieve this using foreach and push like this
result.forEach(element => {
this.state.timestamp.push(element.timestamp);
this.state.size.push(element.size);
});
But i want this array to have the items only from the 10,20,30,40th index alone
I want not all the value. the values should be chosen only in the basis of x+10
Could anyone help me on this
Instead of forEach why not just use a for loop, and on the condition use the modulus % operator with 10? Like if (i % 10 == 0) inside of the for loop, or just increment i by 10 like i+=10.
You could take a for loop and a step for incrementing the index.
const
step = 10,
keys = ["timestamp", "size", "fees", "cost"],
result = Object.fromEntries(keys.map(k => [k, []]));
for (let i = 0; i < size.lenght; i += step) {
keys.forEach(key => result[key].push(size[i][key]));
}
Using forEach is a waste of resources.
You can use for instead:
for(let i=0;i<result.length;i+10){
this.state.timestamp.push(result[i].timestamp);
this.state.size.push(result[i].size);
}
For setting the state you should use setState not just push to it.
let tmp = {
...this.state
}
for(let i=0;i<result.length;i+10){
tmp.size.timestamp.push(result[i].timestamp);
tmp.size.push(result[i].size);
}
this.setState(tmp)
As mentioned by #cybercoder, you probably don't want to change the state variable within the forEach, as that will cause render to be called excessively.
You could simply use a counter and only push elements when the index is divisible by 10:
let i = 0;
let {timestamp, size} = this.state;
result.forEach(element => {
if (i % 10 === 0) {
timestamp.push(element.timestamp);
size.push(element.size);
}
i++;
});
this.setState({
...this.state,
timestamp,
size
});
If you do not want to include the very first (index 0) element:
let i = 0;
let {timestamp, size} = this.state;
result.forEach(element => {
// Exclude very first element
if (i % 10 === 0 && i !== 0) {
timestamp.push(element.timestamp);
size.push(element.size);
}
i++;
});
this.setState({
...this.state,
timestamp,
size
});
I have a function "Next "that maps an array like in the example below and after incrementing the element I am doing something, but I also have another function "Prev" it pretty much does a similar mapping but in that function, I am decrementing the element. The Next function works fine but the Prev function doesn't, can someone please help me with this?
I am mapping an array of object
[
{
"id":"v82b3a65",
"name":"Name"
},
{
"id":"u0b26551",
"name":"Name2"
}
]
my functions :
const Next = () => {
array.items.map((item, key, element) => {
var next = element[key ++];
setId(next.id);
});
};
const Prev = () => {
array.items.map((item, key, element) => {
var prev = element[key --];
setId(prev.id);
});
};
render(
<View>
<Button title={'Prev'} onPress={Prev}/>
<Button title={'Next'} onPress={Next}/>
</View>
)
I am using those functions in onPress of buttons
The result I need: on Next button press I want it to set setID = next objects id and on Prev button press I want to set setID = previous object id
You should check index value if it is larger than 0 in Prev function. Also check if it is smaller than array length - 1 in Next function.
const Next = () => {
var newArray = myArray.map(function (value, index, elements) {
if (index < myArray.length - 1) {
var next = elements[index + 1];
// do something
}
});
};
const Prev = () => {
var newArray = myArray.map(function (value, index, elements) {
if (index > 0) {
var next = elements[index - 1];
// do something
}
});
};
I have a table. When I click on an element I return its index. How can I (starting from this element) display all elements with index + 9. i.e. if the selected element has index 0, then I also need to display elements with indexes 9, 18, 27, etc.
let newCell = [td#2, td#0, td#2, td#0, td#2, ..... td#1, td#0]
newCell.forEach((item, i) => {
item.addEventListener('click', () => {
console.log(i)
})
})
I have not included the code to "display" those elements that will come in the series, but below is the code that logs them out. You can update it to display them.
const DIFF = 9;
newCell.forEach((item, i) => {
item.addEventListener('click', ()=>logAllItemsInSeries(i));
}
const logAllItemsInSeries = (i) => {
console.log(i);
const series = [];
while (i < newCell.length) {
series.push(i);
i += DIFF;
}
series.forEach((index) => {
console.log(index);
console.log(newCell[index]);
});
};
What you need to do in this case is keep track of a count of what elements should be filtered out and then update that count as you filter through it. Unfortunately your cell data is unclear to me so I went ahead and made my own to explain.
let data = [];
for (let i = 0; i < 100; i++) {
data.push({ id: i });
}
function getByNine(id) {
let count = id + 9
return data.filter(i => {
if (i.id === count) {
count = count + 9
return i
} else {
return
}
})
}
console.log(getByNine(3))
Inside your event listener you would put a reference to the getByNine(i) function (or whatever you want to call it) and pass its index.
Inside this function you set a default count of 9 + whatever the id of the element clicked
Run a filter through your array of objects, when the count is correct return that item and then increment the count by 9 for the next correct element
function getByNine(i) {
// code here
}
newCell.forEach((item, i) => {
item.addEventListener('click', () => {
getByNine(i)
})
})
I am building a react project for visualizing insertion sort using redux. I am using react-redux to create and handle actions. However, the problem is that in my insertionSort algorithm, I dispatch an updateArray action every time the array being sorted changes. I put print statements inside the reducer and saw that the state was in fact changing and the action was being dispatched correctly, however, my actual array does not re-render. I put prints inside the relevant UI component's render() function and saw that it was only being called once or twice rather than every time the reducer receives the action. I tried restructuring my code multiple times and reading about similar problems that people have had but their answers did not help me.
Am I just structuring this the wrong way? Should I not be using dispatches every second or so to update my array?
I have a main.js file which is used to render the UI components including my array:
class Main extends React.Component {
setArray = () => {
this.props.setArray(50, window.innerHeight / 1.4)
startSort = () => {
this.props.startSorting(this.props.algorithm, this.props.array)
}
render() {
let { array} = this.props
return (
<div>
<Navbar
startSort={this.startSort}
setArray={this.setArray}
/>
<MainWrapper>
<Row />
</MainWrapper>
</div>
)
}
}
const mapStateToProps = state => {
return {
array: state.array,
}
}
const mapDispatchToProps = dispatch => {
return {
setArray: (length, height) => {
let array = Array.from({ length: length }, () =>
Math.floor(Math.random() * height))
dispatch(setArray(array))
},
startSorting: (algorithm, array) => {
var doSort
if (algorithm == 'insertionSort') {
doSort = insertionSort
}
doSort(array, dispatch)
}
}
}
My actual array is generated with Row.js
class Row extends React.Component {
generateNodes(array) {
var elements = []
array.forEach((value, index) => {
elements.push(
<CenteredColumn>
<ArrayNode idx={index} value={value} />
</CenteredColumn>
)
})
return elements
}
render() {
let { array } = this.props
console.log('UPDATED ARRAY: ' + array)
var arrayElements = this.generateNodes(array)
return <RowWrapper>{arrayElements}</RowWrapper>
}
}
const mapStateToProps = state => {
return {
array: state.array
}
}
And finally, my actual algoritm is in insertionSort.js in which I import my actions from their reducers and pass in a dispatch function from main.js:
function delayedInsertion(array, dispatch) {
let n = array.length
var i = 0
function loop() {
setTimeout(function() {
var temp = array[i]
var j = i - 1
while (j >= 0 && array[j] > temp) {
array[j + 1] = array[j]
j--
}
array[j + 1] = temp
// console.log('ARRAY: ' + array)
dispatch(updateArray(array))
i++
if (i < n) {
loop()
}
}, 200)
}
loop()
console.log('DONE')
}
It seems that you are mutating your state.
You are passing this.props.array to your doSort action and as I understand your idea correctly, you are just calling delayedInsertion from that action (you did not post source code of that action).
But in delayedInsertion you are mutating the passed array when you are changing positions of you items, here:
while (j >= 0 && array[j] > temp) {
array[j + 1] = array[j]
j--
}
array[j + 1] = temp
You need to perform immutable change of the array.