javascript through Loop inside React JSX
react loop through object (24)
I'm trying to do something like the following in React JSX (where ObjectRow is a separate component):
<tbody> for (var i=0; i < numrows; i++) { <ObjectRow/> } </tbody>
I realize and understand why this isn't valid JSX, since JSX maps to function calls. However, coming from template land and being new to JSX, I am unsure how I would achieve the above (adding a component multiple times).
Simply use
.map() to loop through your collection and return
<ObjectRow> items with props from each iteration.
Assuming
objects is an array somewhere...
<tbody> { objects.map((obj, index) => <ObjectRow obj={ obj } key={ index }/> ) } </tbody>
Not sure if this will work for your situation, but>
I use it like
<tbody> { numrows ? ( numrows.map(obj => { return <ObjectRow /> }) ) : null} </tbody>
I know this is an old thread, but you might want to checkout, which does let you use jsx-style templates in react, with a few directives (such as rt-repeat).
Your example, if you used react-templates, would be:
<tbody> <ObjectRow rt- </tbody>
You can also use a self-invoking function:
return <tbody> {(() => { let row = [] for (var i = 0; i < numrows; i++) { row.push(<ObjectRow key={i} />) } return row })()} </tbody>
if numrows is a array, and it's very simple.
<tbody> {numrows.map(item => <ObjectRow />)} </tbody>
Array data type in React is very better, array can back new array, and support filter, reduce etc.
If you opt to convert this inside return( ) of render method, easiest option would be using map( ) method. Map your array into JSX syntax using map() function, as shown below (ES6 syntax is used).
Inside parent component:
<tbody>
{ objectArray.map((object, index) => <ObjectRow key={index} object={object}>) }
</tbody>
Please note the
key attribute added to your child component. If you didn't provide key attribute, you can see the following warning on your console.
Warning: Each child in an array or iterator should have a unique "key" prop.
Now at the ObjectRow component you can access the object from it's properties.
Inside ObjectRow component
const { object } = this.props
or
const object = this.props.object
This should fetch you the object you passed from parent component to the variable
object in ObjectRow component. Now you can spit out the values in that object according to your purpose.
References :
map() method in Javascript
Here's a simple solution to it.
var Object_rows=[]; for (var i=0; i < numrows; i++) { Object_rows.push(<ObjectRow/>) } <tbody> {Object_rows} </tbody>
No mapping and complex code required.You just need to push the rows to array and return the values to render it.
Simply using map Array method with ES6 syntax:
<tbody> {items.map(item => <ObjectRow key={item.id} name={item.name} />)} </tbody>
Don't forget the
key property.
If you're already using lodash, the
_.times function is handy.
import React, { Component } from 'react'; import Select from './Select'; import _ from 'lodash'; export default class App extends Component { render() { return ( <div className="container"> <ol> {_.times(3, i => <li key={i}> <Select onSelect={this.onSelect}> <option value="1">bacon</option> <option value="2">cheez</option> </Select> </li> )} </ol> </div> ); } }
There are several answers pointing to using the
map statement. Here is a complete example using an iterator within the FeatureList component to list Feature components based on a JSON data structure called features.
const FeatureList = ({ features, onClickFeature, onClickLikes }) => ( <div className="feature-list"> {features.map(feature => <Feature key={feature.id} {...feature} onClickFeature={() => onClickFeature(feature.id)} onClickLikes={() => onClickLikes(feature.id)} /> )} </div> );
You can view the complete FeatureList code on GitHub. The features fixture is listed here.
Your JSX code will compile into pure JavaScript code, any tags will be replaced by
ReactElement objects. In JavaScript, you cannot call a function multiple times to collect their returned variables.
It is illegal, the only way is to use an array to store the function returned variables.
Or you can use
Array.prototype.map which is available since JavaScript ES5 to handle this situation.
Maybe we can write other compiler to recreate a new JSX syntax to implement a repeat function just like Angular's
ng-repeat.
ES2015 Array.from with the map function + key
If you have nothing to
.map() you can use
Array.from() with the
mapFn to repeat elements:
<tbody> {Array.from({ length: 5 }, (v, k) => <ObjectRow key={k} />)} </tbody>. Map function creates a unique index from 0 but it's not recommended using the index but if your value is unique or if there is a unique key, you can use them:
<tbody> {numrows.map(x=> <ObjectRow key={x.id} />)} </tbody>
Also few line from MDN if you not familiar with map function on Array:, which have been deleted or which have never been assigned a value).
callback is invoked with three arguments: the value of the element, the index of the element, and the Array object being traversed.
If a thisArg parameter is provided to map, it will be used as callback's this value. Otherwise, the value undefined will be used as its this value. This value ultimately observable by the callback is determined according to the usual rules for determining the this seen by a function.
map does not mutate the array on which it is called (although callback, if invoked, may do so).
You can do something like:
let foo = [1,undefined,3] { foo.map(e => !!e ? <Object /> : null )}
...Or you can also prepare an array of objects and map it to a function to have the desired output. I prefer this, because it helps me to maintain the good practice of coding with no logic inside the return of render.
render() { const mapItem = []; for(let i =0;i<item.length;i++) mapItem.push(i); const singleItem => (item, index) { // item the single item in the array // the index of the item in the array // can implement any logic here return ( <ObjectRow/> ) } return( <tbody>{mapItem.map(singleItem)}</tbody> ) }
you can of course solve with a .map as suggested by the other answer. If you already use babel, you could think about using jsx-control-statements They require a little of setting, but I think it's worth in terms of readability (especially for non-react developer). If you use a linter, there's also eslint-plugin-jsx-control-statements
There are multiple ways to go about doing this. JSX eventually gets compiled to JavaScript, so as long as you're writing valid JavaScript, you'll be good.
My answer aims to consolidate all the wonderful ways already presented here:
If you do not have an array of object, simply the number of rows:
within the
return block, creating an
Array and using
Array.prototype.map:
render() { return ( <tbody> {Array(numrows).fill(null).map((value, index) => ( <ObjectRow key={index}> ))} </tbody> ); }
outside the
return block, simply use a normal JavaScript for-loop:
render() { let rows = []; for (let i = 0; i < numrows; i++) { rows.push(<ObjectRow key={i}/>); } return ( <tbody>{rows}</tbody> ); }
immediately invoked function expression:
render() { return ( <tbody> {() => { let rows = []; for (let i = 0; i < numrows; i++) { rows.push(<ObjectRow key={i}/>); } return rows; }} </tbody> ); }
If you have an array of objects
within the
return block,
.map() each object to a
<ObjectRow> component:
render() { return ( <tbody> {objectRows.map((row, index) => ( <ObjectRow key={index} data={row} /> ))} </tbody> ); }
outside the
return block, simply use a normal JavaScript for-loop:
render() { let rows = []; for (let i = 0; i < objectRows.length; i++) { rows.push(<ObjectRow key={i} data={objectRows[i]} />); } return ( <tbody>{rows}</tbody> ); }
immediately invoked function expression:
render() { return ( <tbody> {() => { let rows = []; for (let i = 0; i < objectRows.length; i++) { rows.push(<ObjectRow key={i} data={objectRows[i]} />); } return rows; }} </tbody> ); }
I tend to favor an approach where programming logic happens outside the return value of
render. This helps keep what is actually rendered easy to grok.
So I'd probably do something like:
import _ from 'lodash'; ... const TableBody = ({ objects }) => { const objectRows = objects.map(obj => <ObjectRow object={obj} />); return <tbody>{objectRows}</tbody>; }
Admittedly this is such a small amount of code that inlining it might work fine.
Think of it like you're just calling JavaScript functions. You can't put a
for loop inside a function call:
return tbody( for (var i = 0; i < numrows; i++) { ObjectRow() } )
But you can make an array, and then pass that in:, my JavaScript example is almost exactly what that example of JSX transforms into. Play around with Babel REPL to get a feel for how JSX works.
Here is a sample from React doc:
function Item(props) { return <li>{props.message}</li>; } function TodoList() { const todos = ['finish doc', 'submit pr', 'nag dan to review']; return ( <ul> {todos.map((message) => <Item key={message} message={message} />)} </ul> ); }
as your case, I suggest writing like this:
function render() { return ( <tbody> {numrows.map((roe, index) => <ObjectRow key={index} />)} </tbody> ); }
Please notice the Key is very important, because React use Key to differ data in array.
I use this:
gridItems = this.state.applications.map(app => <ApplicationItem key={app.Id} app={app } /> );
PD: never forget the key or you will have a lot of warnings !
Since you are writing Javascript syntax inside JSX code, you need to wrap your Javascript in curly braces.
row = () => { var rows = []; for (let i = 0; i<numrows; i++) { rows.push(<ObjectRow/>); } return rows; } <tbody> {this.row()} </tbody>