1

I am new to ReactJS, I am trying to create 10 buttons with for loops, and set a variable with the integer from the for loop, however the function setMqty(i) always returns 11 for all the buttons. What is the correct way for doing this?

  var qtyButtons = [];
  for(var i = 1; i < 11; i++) {
    qtyButtons.push(<div
        onClick={(btn) => {qtyBtnToggle(btn);setMqty(i); }}
    >
      {i}
    </div>)
  }

Thank you in advance.

Charas
  • 1,753
  • 4
  • 21
  • 53
  • I am guessing setMqty is a state .can you try setMqty(currentValue=> currentValue+1) – pranit Feb 22 '22 at 16:53
  • 4
    `setMqty(i)` always passes the last value of `i`, because `for (var i = 1;` uses a single `i` for all iterations. So whenever the callback is invoked they all refer to the same `i`, which is `11` after the loop is over. Use `let` instead of `var`. `for (let i = 1;` will create a separate `i` for each iteration. See: [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – 3limin4t0r Feb 22 '22 at 16:57
  • Nice @3limin4t0r Thanks mate, it works now, such a simple solution! Please put this as an answer and I will accept it gladly. Thanks! – Charas Feb 23 '22 at 08:27

3 Answers3

1

I think using a map function is the correct way for doing it.

Therefore, you must generate a range of numbers our any element to be able to perform a map function.

So if i was in your place i would have do it like:

const [mqty, setMqty] = useState(0);

// ...

const qtyButtons = Array(10).fill().map((_, i) => {
  const value = parseInt(i);

  return (
    <button onClick={handleClick(value)}>
      {value}
    </button>
  );
});

// ...
// The trick here is that function returning another function, beware of that
const handleClick = (i) => () => {
  setMqty(i);
};
Ritchi
  • 161
  • 2
  • 6
1

The main issue here has to do with scoping. Let me reduce your example to a more minimal example:

var qtyCallbacks = [];
for (var i = 1; i < 11; i++) {
  qtyCallbacks.push(() => console.log(i));
}

for (const callback of qtyCallbacks) {
  callback();
}

What's going wrong here? Why is it only logging 11 and not 1-10?

The issue is that a variable defined with var are scoped to the nearest function or globally if not in a function. So what does this mean?

It means that each iteration in your for loop refers to the same i. This isn't an issue if you directly use i.

for (var i = 1; i < 11; i++) {
  console.log(i); // logs 1-10
}

However it does become an issue if you refer to i in code that is executed at a later point. The reason the initial snippet only logs 11 is because all the callbacks are created, each one referring to the same i. So when you evoke each callback after the for-loop is complete, they will all refer to the current value (which is 11).

for (var i = 1; i < 11; i++) {
}

console.log(i); // logs 11
// so if we invoke the callbacks here they all log 11

So how do you solve this issue? The simplest solution is probably to replace var with let. let will create a new variable for each for-iteration. Meaning that each created callback will refer to their own i and not a shared i. This also means that you cannot access i outside of the for-loop.

var qtyCallbacks = [];
for (let i = 1; i < 11; i++) {
  qtyCallbacks.push(() => console.log(i));
}

for (const callback of qtyCallbacks) {
  callback();
}

console.log(i); // expected error, `i` is only available within the for-block
3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
  • Thank you very much for this very comprehensive answer sir! Hope this will help others too :) – Charas Feb 24 '22 at 15:00
0

I am guessing setMqty is a state.Can you try

setMqty(currentValue=> currentValue+1)
pranit
  • 128
  • 1
  • 1
  • 8