6

Let's say I have a component, like:

<FormInput label="Phone Number" />

and I want to draw N number of them on the UI. What would the best practice for accomplishing this be? My initial thought is to create an array of N members so I can use map, like:

var myMapTrigger = [0,0,0,0,0]
myMapTrigger.map(function(){
  return (<FormInput label="Phone Number" />)
}

This is of course a little hacky though.Is there a more idiomatic way that is more "thinking React"?

Thomas Murphy
  • 1,360
  • 4
  • 14
  • 41
  • 1
    Not really a React question.. more like a "how do I do something N times" arbitrarily in JavaScript. You can use / create a range function or do something like this http://stackoverflow.com/questions/1295584/most-efficient-way-to-create-a-zero-filled-javascript-array – azium Jun 16 '16 at 15:20
  • @azium I acknowledge that, this is something I haven't encountered in the React docs/community yet, so wanted to both honestly ask and have a discussion if their is best practice. – Thomas Murphy Jun 16 '16 at 15:24

4 Answers4

3

If you're open to using Lodash, then _.times would work well.

_.times(N, i => <FormInput key={i} label="Phone Number" />);

(Don't forget to specify a key to make React happy.)

Josh Kelley
  • 56,064
  • 19
  • 146
  • 246
3

Two years later, here's the most parsimonious way I know of to make an array containing n elements in native JS.

const duplicate = (x, n) => Array.from(new Array(n), () => x);

const n = 3;
const oneComp = <span>Unicorns &amp; Rainbows</span>;
const nComps = duplicate(oneComp, n);

class FormInputGroup extends React.Component {

    // ...

    render() {

        // ...

        return <div>{nComps}</div>;
    }

    // or, in this case,

    render = () => <div>{nComps}</div>;
}

// or even

const ComponentGroup = () => <div>{nComps}</div>;

// renders as "Unicorns & RainbowsUnicorns & RainbowsUnicorns & Rainbows"

In case you're wondering why not just use new Array(n), it's because that expression has length n, but contains 0 elements — i.e.

const trashArray = new Array(n);

console.log(trashArray.length) // prints `n`
console.log(Object.keys(trashArray)) // prints `[]`

— this means that trashArray.map(func) will always yield an empty array (but one with .length = n, thanks JavaScript).

(If they're inline and you want to put, say, <br />s between them but not after the last one, you can do something like

const zipInLineBreaks = tags => tags.reduce((accum, currentEl, i, arr) => accum
    .concat([currentEl]) // or, in this case, `[oneComp]`
    .concat((i === arr.length - 1)
        ? []
        : [<br /]
    ), []);


const children = zipInLineBreaks(nComps);

Group = () => <div>{children}</div>;

/* 
 * "Unicorns & Rainbows
 *  Unicorns & Rainbows
 *  Unicorns & Rainbows"
 */

.)

See this sandbox for an example where the repeated element is a React Component.

Alex Ruble
  • 31
  • 2
3

This hack is pretty concise.

Array.from(Array(N)).map(() => {...})

Disclaimer: don't do this if N approaches infinity.

1

Something like that, if you dont want to include lodash:

renderForm() {

    const times = 5;
    const inputs = [];

    for(var i=0; i<times;i++) {
        inputs.push(<FormInput key={i} label="Phone Number" />);
    }
    return inputs;
}

render() {
    return <div>{this.renderForm()}</div>;
}
mrlew
  • 7,078
  • 3
  • 25
  • 28
  • That's pretty similar to the solution I ended up going with, though I did it with map() instead of an explicit for loop. I don't think times and inputs should be const though…while I know that const doesn't make the rVal immutable, I think convention suggests they should be var – Thomas Murphy Jun 17 '16 at 13:34
  • 1
    Convention says to use const everytime time you know the reference wont change (in case of objects, arrays). Otherwise, use let. I used for because there's no array to iterate. It's a times counter. But this is just a way to accomplish the goal. There are others, of course. – mrlew Jun 17 '16 at 15:22