Can you use a while loop in React? [duplicate] - javascript

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 27 days ago.
Problem : I am trying to create an array of 4 things from a list but the while loop always produces an infinite loop.
const [options, setOptions] = useState([]);
const getThings = () => {
while(options.length < 4) {
let randomThing =
listOfThings[Math.floor(Math.random()*listOfThings.length)];
!options.includes(randomThing) && setOptions([...options, randomThing]);
}
};
I believe the problem is connected to another issue - when I call the function once, it is randomly called anywhere between 2 - 9 times even without the while loop attached. Still trying to figure out why it keeps randomly firing so much.
getThings();

setState does not instantly change any values. Create the entire array first, then set the state at the end of the loop.
const getThings = () => {
let newOptions = [...options];
while (newOptions.length < 4) {
let randomThing = listOfThings[Math.floor(Math.random()*listOfThings.length)];
if (!newOptions.includes(randomThing)) newOptions.push(randomThing);
}
setOptions(newOptions);
};

The error is caused because you are calling setOptions inside a loop, you could cache a new output in the function and then call the hook at the end
https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

Related

How to store components in an array and render them when I need in React?

I want to render x numbers times a specific component according to what the user chooses.
I wrote this function:
const generateShiftsForm = (totalWeeks) => {
const shiftWeeks = 1;
const shiftList = [];
for (let i = shiftWeeks; i === totalWeeks; i++) {
shiftList.push(<ShiftForm totalMinutes={totalMinutes} setTotalMinutes={setTotalMinutes}/>);
}
return shiftList;
};
But when I call it inside the render function it doesn't generate anything unless the totalWeeks is 1.
{generateShiftsForm(shiftNumber).map((form) => (form))}
Why is it like this? I didn't get it, can someone explain it to me?
Seems like there is a typo in the for loop condition - i === totalWeeks, that's why the for loop is being run only once and only if totalWeeks equals 1. Try replacing the for loop you have with the following:
for (let i = shiftWeeks; i <= totalWeeks; i++) {...}

Problem with adding event listener in loop [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 2 years ago.
I have a list of elements and I'd like to add a click event to each one that will call a function and pass it an index of a list. In the case below I'm defining itemIndex as 0 and incrementing it each loop. When the click event is called the index it uses is 3 (number of loops +1) instead of the number of the loop. How can I solve this problem?
let itemIndex = 0;
itemList.forEach(item => {
const itemFragment = document.importNode(this.itemTemplate.content, true);
//add click event
itemFragment.querySelector('.part').addEventListener('click', () => {
loadItem(itemList[itemIndex]);
});
//append product to body row
itemTarget.appendChild(itemFragment);
itemIndex++;
});
Just use the index argument in forEach. Currently, you are incrementing the same outer variable on each iteration, so each event handler accesses the same value.
itemList.forEach((item, itemIndex) => {
const itemFragment = document.importNode(this.itemTemplate.content, true);
//add click event
itemFragment.querySelector('.part').addEventListener('click', () => {
loadItem(itemList[itemIndex]);
});
//append product to body row
itemTarget.appendChild(itemFragment);
});
In your case though, you don't actually need the index itself but the item on each iteration, so just pass item to loadItem.
itemList.forEach(item => {
const itemFragment = document.importNode(this.itemTemplate.content, true);
//add click event
itemFragment.querySelector('.part').addEventListener('click', () => {
loadItem(item);
});
//append product to body row
itemTarget.appendChild(itemFragment);
});

Set state and then reading the state shows the previous value [duplicate]

This question already has answers here:
React setState not updating state
(11 answers)
Closed 3 years ago.
I have the following code that maintains the value when the textbox value is changed. However, whilst debugging the valueHasChangedEvent the variable x line shown below holds the previous value strangely. Is there something I'm doing wrong? The example shown is when I enter 'test123' into the textbox.
Thanks
onChange event
<Input onChange={this.valueHasChangedEvent}
type="text"
name="test"
id="test" />
Method
valueHasChangedEvent = (event) => {
var self = this;
const { name, value } = event.target;
self.setState({test: value}); // value = 'test123'
var x = self.state.test; // x = 'test12'
}
State needs some time to change, and since you are reading the state value before the state mutates, you get the previous value as output. So you need to write it in the callback to the setState function or read it in shouldComponentUpdate()
var x;
self.setState({test: value}, (x) => {
x = self.state.test
});

Calling event listener using a FOR loop javascript [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
I have a question on the following for loop in javascript - the purpose of the for loop is simple, they are meant to listen to the event for all of the columns that i have.
I have two methods to achieve this, i wonder why one works but not another.
First method that DOES NOT work:
var column = document.querySelectorAll("td");
for (var i =0 ; i< column.length; i++)//column.length is 9
{
column[i].addEventListener("click",function(){
column[i].innerText = "X";
})
}
it prints out the following error when the event is triggered:
Uncaught TypeError: Cannot set property 'innerText' of undefined
at HTMLTableCellElement. (:6:21)
I replaced "column[i].innerText = "X" with console.log(i), i get 9.
But according to my for loop condition, it is supposed to end when i reaches 8 as my column.length is 9, and i use i < column.length, so it should stop at 8.
Question: Why can i get 9 in this for loop ? And why my second approach below can work ?
Second method that DOES work:
var column = document.querySelectorAll("td");
for ( var i = 0 ; i < column.length; i++ )
{
column[i] = clickAction(i);
}
function clickAction(param)
{
column[param].addEventListener("click",function(){
column[param].innerText = "X";
})
}
It works fine if i put the action into a function externally.
Thanks
In the first method when your loop exist that time value of i is 9 and it register events on all column with value 9. So when you fire the event it throws an error.
i found out that to avoid this problem, other than calling the function externally, i can also do the following:
var column = document.querySelectorAll("td");
for (var i =0 ; i< column.length; i++)//column.length is 9
{
column[i].addEventListener("click",function(){
this.innerText = "X"; //using this keyword here so that current object is being called.
})
}

Value is not being passed to for loop [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
This is a bit of a mystery for me. I have two functions:
1)
var revisionNumber;
var $list = $('<ul>');
TFS_Wit_WebApi.getClient().getWorkItem(284)
.then(function(query) {
revisionNumber = query.rev;
});
2)
for (i = 0; i < revisionNumber; i++) {
TFS_Wit_WebApi.getClient().getRevision(284, 6)
.then(function(query) {
$list.append($('<li>').text("story board" + revisionNumber));
});
}
The reivisonNumber value is supposed to be 15. When in for loop I put instead of the variable a number 15, the second function works just fine as well as for loop and it actually displays this number 15.
If I remove for loop, it also works and displays the value of revisionNumber variable from the first function.
However, when I put revisionNumber in my for loop, the second function does not work at all.
Why is it not going inside the second function with the above for loop?
The for loop is probably being executed before the getWorkItem().then() callback is being executed. You will need to wait for that callback to run before running the for loop, either by moving it into the callback function, or putting it in it's own function and calling that function in the callback.
For example:
var revisionNumber;
var $list = $('<ul>');
TFS_Wit_WebApi.getClient().getWorkItem(284).then(function (query) {
revisionNumber = query.rev;
for (i = 0; i < revisionNumber; i++) {
TFS_Wit_WebApi.getClient().getRevision(284, 6).then(function (query) {
$list.append($('<li>').text("story board" + revisionNumber));
});
}
});

Categories