For loops ever in React?

Hey guys, weird function I got going on here that I put in it’s own component. If I pass in a number, it creates that many spans. Is there any way not to use a for loop here or is this the only time when you have nothing to iterate over?

import React from 'react'

const SpanMaker = ({ num }) => {
  let spans = []
  
  for (let i = 0; i < num; i++) {
    spans.push(<span key={i}></span>)
  }
  return spans
}

export default SpanMaker

I think another thing to note here is that I am not updating this or using it in state. I am simply calling the function 3 times with hardcoded numbers.

This looks correct to me. Your input is just a number and a for loop is a good option to use.

Other options could be a while loop or recursion; but i’d personally go the same route you have.

For loops are fine, they’re quite performant.

If you really, really wanted to avoid a for loop you could do this:

new Array(num)
  .fill(undefined)
  .map((_, i) => (<span key={i} />));

I suggest you stick with what you’ve got.

1 Like

Another one-line solution like @colinthornton suggested, but uses Array.from that gets a map as a second argument.

So a simple SFC that spits out spans would be

/**
 * 
 * @param {Object} props
 * @param {number} props.n
 */
const Spans = ({n}) =>
Array.from({length: n}, (_, i) => (<span key={i}></span>));

Same with the other posters, I have nothing against a for loop.

1 Like

Usually for-loops in react are interchanged with higher order functions like map and filter; Array.from is good for generating indexes out of nowhere yeah.

1 Like

These are some great responses for the OP and myself as well. Thank you

I’m not really familiar or just .from or .fill methods, and learned new things tonight :slight_smile:

But in a situation such as the OPs, when only a Number is the input. Why would you want to convert to Array then map over it?

If the number was huge, it seems like an extra process and memory to achieve the same result.

I do appreciate the new things i’ve learned, just wondering if the Array methods are more efficient/preferred or if just for an example of an alternative solution.

You have to note, that React should not be used this way, it seems you have conceptual misunderstanding what React is for… React by itself is your SpanOrWhateverMaker, you don’t need to re-create React’s functionality

I appreciate it. The former problem I had I understand how I was overabstracting, but this problem is a very one time use case it seems like.

You also should not be setting the key attribute to the loop iterator, because it changes every time you add or remove an element from the loop. It’s worse than having no key at all. It should be assigned to a string that is unique to the object, such as a name or some other stable identifier.

Well, it indeed depends on the use case. Essential outcome of React is render(), so if you just want to render a number of empty span elements to the document, it would be still strange, but the possible way to go. As long as you don’t have another component that will try to select these spans and append some data into them, it’s more or less reasonable

Struggling to find a unique option to pass into here.

Typically it’s the content of the element of the list, or some unique element of it. Hard to tell with a contrived example using blank spans. And yes sometimes it’s infuriatingly tricky to pick out when the list component tries to be generic, but usually there’s some common property you can pick out regardless.

If you’re really stuck for a key, you can get away with using an index to shut up the warning (it’s React’s default behavior anyway) and if you only have a few (dozen) elements, it’ll perform fine. It’s just universally considered an antipattern since the keys don’t remain as stable as they should; once any item indexed that way is added or removed, React is forced to re-render everything after that item, since all their keys have changed.

Absolutely understandable. The entirety of this damn thing is to create this from scratch. Every shape is a span and I literally had 73 <span>s hardcoded to achieve it:

There are a lot of ways to generate pseudo random strings. This is sitting in front of me in one of my projects for example (I think pinched from SO):

function browserId (numChars = 40) {
  const src = new Uint8Array(numChars / 2);
  window.crypto.getRandomValues(src);
  return [...src].map(num => `0${num.toString(16)}`.substr(-2)). join ("");
}

If you can’t think of how to get uniques keys from what you have, just generate a random key

Unless you really need cryptographically strong random IDs, I’d recommend Math.random() instead. crypto has to do I/O to use the system’s cryptographic RNG, and while those don’t block these days, it’s still a system call. Also, you have to make sure to generate those IDs only once, since generating a new ID per pass would kind of defeat the purpose. All in all, a UUID would probably be better.

So, installing UUID as a dependency into my project and using all that code is seriously better practice? Not that I’m trying to be snarky, I’m really trying to understand better practices and efficiency at this point.

Your code with for-loop is perfectly fine.

You can also generalize it to something like this:

//  n       => How many times you want to run this loop?
// callback => Function to run on every iteration.
const times = (n = 1, callback) => {
  let result = []
  for(let i = 0; i < n; i++) {
    result.push(callback(i))
  }
  return result
}

// Use it like so
const listOfSpans = times(5, i => <span key={i}>Hello!</span>)
const listOfDivs = times(5, i => <div key={i}>Hello!</div>)

And yes, avoid using index as the key whenever possible.

1 Like

Oh this is awsesome, thank you.

Nah. I’m saying it’s a better source of unique ids than anything people are likely to come up with on their own when it comes to generated ids. Still has the same issue that you’d need to generate them only once rather than computing them from the content, which is really what component keys are all about. An index really is is fine when you have less than, say, a hundred items, it’s just not the pattern you want to be using for everything.

1 Like

Yeah, I am absolutely avoiding everywhere else in the project besides this one case. Thank you for the tips!