I want to create n instances of a React component.
What is a good terse way to do this give that JSX can only contain expressions?
I currently am trying the following:
<Wrapper>
{repeat(n)(i => <MyComponent i={i} />}
</Wrapper>
function repeat(n) {
return cb => Array(n).fill(null).forEach((_, i) => cb(i));
}
You can use as much JavaScript als you like :)
render() {
const lst = [1, 2, 3, 4];
return (
<div>
{lst.map(itm => <span key={itm}>{itm}</span>)}
</div>
);
}
If you do not have a key ready, you can use the second argument of the map callback which is the index in the array. More info on MDN.
In your specific case where you do not have an array but just a number:
render() {
var times = [];
for (let i = 0; i < 10; i++) {
times.push(<MyComponent key={i} i={i} />);
}
return <Wrapper>{times}</Wrapper>;
}
Also check this answer on how to use for loops. It's not quite as nice but it also works. I believe the React team has planned to make working with arrays in JSX more straight forward.
If you have just a number, and do not want to use a for loop, you could also "fake" it, for example by using string.repeat. Not sure if this is very readable though :)
render() {
return (
<div>
{'a'.repeat(10).split('').map((_, i) => <MyComponent i={i} />}
</div>
);
}
Related
I'm trying to understand how closures work in javascript.
Here, I'm iterating through values of the 'items' array using a foreach. I've a let variable "count" declared outside the scope of the foreach loop. After each iteration, the value of 'item' is added to 'count'. Inside the loop, I'm declaring a new React Component that renders the value of "count".
export default function App() {
const comps: ComponentType<any>[] = [];
let count = 0;
const items = [5, 1, 3];
items.forEach((item) => {
const countCopy = count;
comps.push((props) => (
<div>
{count} : {countCopy}
</div>
));
count += item;
});
return (
<div className="App">
{comps.map((Comp) => (
<Comp />
))}
</div>
);
}
But when I run the code, the count value that is showed is its last value (9). I need to create a copy "countCopy" of the variable in order to display its current value. Why is that? Is there a cleaner way?
CodeSandbox:
https://codesandbox.io/s/javascript-closure-question-5uwmm
I've found a solution using reduce instead of foreach:
export default function App() {
const comps: ComponentType<any>[] = [];
const items = [5, 1, 3];
items.reduce<number>((count, item) => {
const countCopy = count;
comps.push((props) => (
<div>
{count} : {countCopy}
</div>
));
return count + item;
}, 0);
return (
<div className="App">
{comps.map((Comp) => (
<Comp />
))}
</div>
);
}
The "count" that is rendered is assured to be the current value, since it is declared inside the loop
https://codesandbox.io/s/javascript-closure-question-solution-8ping
i'm trying to render some things in my select_types function.
For that, i'm calling my function in a loop.
But my problem is : I can't return more than one time in a loop, but i need to render more than one thing.
for (let [keys, values] of Object.entries(test1[0])) {
return (
<View>
{this.select_types(values.type, keys)}
</View>
)
}
Do i need to replace all my loop ? Can I keep my loop but do multiple return in react native ?
Try saving all Views in an array, then return them, something like:
const results = [];
for (let [keys, values] of Object.entries(test1[0])) {
results.push(
<View>
{this.select_types(values.type, keys)}
</View>
);
}
return results;
I need to be able to loop over a number and return some jsx. For example
<ul>
{for(i =0; i < 10; i++){
return <li>{i}</li>
}}
</ul>
This is not exactly what I want to do, but if I can solve this then I should be able to complete what I need to do. This however returns expression expected on the for. I have done some research and people say you can't use for loops inside of jsx because they do not return anything.
How do I go about looping over a number to return some amount of jsx?
You could use Array.from() instead.
let App = () => {
return <ul>{Array.from(Array(10), (e, i) => {
return <li key={i}>{i}</li>
})}</ul>
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<script src="https://unpkg.com/react#16.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.1.0/umd/react-dom.development.js"></script>
<div id="app"></div>
You can also use ES6 spread syntax with map() method.
let App = () => {
return <ul>{[...Array(10)].map((e, i) => {
return <li key={i}>{i}</li>
})}</ul>
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<script src="https://unpkg.com/react#16.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.1.0/umd/react-dom.development.js"></script>
<div id="app"></div>
You can do it like this :
createElements(n){
var elements = [];
for(i =0; i < n; i++){
elements.push(<li>{i}</li>);
}
return elements;
}
<ul>
{this.createElements(20)}
</ul>
You need to use recursive iterators such as map, forEach, filter etc. If you really need to do something like this you can
const statelessComp = props => {
let arr = Array.apply(null, {length: 10}).map(Number.call, Number);
return (
<ul>
{arr.map(item => {
return <li>{item}</li>
})}
</ul>
)
}
edit: these are the functions you should familiarize yourself with
This is the simple way
createElements(number){
var elements = [];
for(i =0; i < number; i++){
elements.push(<div className="list-item">{i} - Some text or element</div>);
}
return elements;
}
In return statement
<div>
{this.createElements(10)}
</div>
you can use
<ul>
<li>{[...Array.from(Array(10).keys())].map((num, i) => <p key={i}>{num}</p>)}</li>
</ul>
This worked for me
https://www.codegrepper.com/code-examples/javascript/react+loop+number+of+times
const n = 8;
{[...Array(n)].map((elementInArray, index) => (
<div className="" key={index}> Whatever needs to be rendered repeatedly... </div>
)
)}
If you don't mind adding a very common and useful library, you can use lodash and map in this situation and it will be nice and clean.
import _ from "lodash";
return (
<ul>{_.range(1, 10).map(i => (<li>i</li>))}<ul>
)
very simple question, when I loop through array when rendering react compnent with .map function, say:
render() {
let board = this.props.board;
return (
<div>
{
board.map((piece,index) => {
return (
<Piece data={piece}/>
);
})
}
</div>
);
}
I'm trying to add a break line every 5 pieces so (index % 5 == 0) add <br /> before the <Piece />
when I try to concatinate with + or to do something like this:
board.map((piece,index) => {
return (
(index % 5 == 0) ? <br /> : ''
<Piece data={piece}/>
);
})
I'm getting an output of matrix of [Object object]'s
Return an array of [ <br />, <Piece> ] if the condition holds and return just the Piece component otherwise. See the fiddle.
The relevant piece of code is this:
return <div>{items.map(function (i, index) {
if (index % 5 === 0) {
return [ <br key={index + "break"} />, <Item key={index} num={i} /> ];
}
return <Item key={index} num={i} />;
})}</div>;
Also, put key on the components you return from map or other ways that return array-like instances. This way, React doesn't need to take out all the generated components and replace them on each render, but can just find them under the key and update their attributes. Check out Reconciliation in React docs to learn more.
I'm trying to convert a jQuery component to React.js and one of the things I'm having difficulty with is rendering n number of elements based on a for loop.
I understand this is not possible, or recommended and that where an array exists in the model it makes complete sense to use map. That's fine, but what about when you do not have an array? Instead you have numeric value which equates to a given number of elements to render, then what should you do?
Here's my example, I want to prefix a element with an arbitrary number of span tags based on it's hierarchical level. So at level 3, I want 3 span tags before the text element.
In javascript:
for (var i = 0; i < level; i++) {
$el.append('<span class="indent"></span>');
}
$el.append('Some text value');
I can't seem to get this, or anything similar to work in a JSX React.js component. Instead I had to do the following, first building a temp array to the correct length and then looping the array.
React.js
render: function() {
var tmp = [];
for (var i = 0; i < this.props.level; i++) {
tmp.push(i);
}
var indents = tmp.map(function (i) {
return (
<span className='indent'></span>
);
});
return (
...
{indents}
"Some text value"
...
);
}
Surely this can't be the best, or only way to achieve this? What am I missing?
Updated: As of React > 0.16
Render method does not necessarily have to return a single element. An array can also be returned.
var indents = [];
for (var i = 0; i < this.props.level; i++) {
indents.push(<span className='indent' key={i}></span>);
}
return indents;
OR
return this.props.level.map((item, index) => (
<span className="indent" key={index}>
{index}
</span>
));
Docs here explaining about JSX children
OLD:
You can use one loop instead
var indents = [];
for (var i = 0; i < this.props.level; i++) {
indents.push(<span className='indent' key={i}></span>);
}
return (
<div>
{indents}
"Some text value"
</div>
);
You can also use .map and fancy es6
return (
<div>
{this.props.level.map((item, index) => (
<span className='indent' key={index} />
))}
"Some text value"
</div>
);
Also, you have to wrap the return value in a container. I used div in the above example
As the docs say here
Currently, in a component's render, you can only return one node; if you have, say, a list of divs to return, you must wrap your components within a div, span or any other component.
Here is more functional example with some ES6 features:
'use strict';
const React = require('react');
function renderArticles(articles) {
if (articles.length > 0) {
return articles.map((article, index) => (
<Article key={index} article={article} />
));
}
else return [];
}
const Article = ({article}) => {
return (
<article key={article.id}>
<a href={article.link}>{article.title}</a>
<p>{article.description}</p>
</article>
);
};
const Articles = React.createClass({
render() {
const articles = renderArticles(this.props.articles);
return (
<section>
{ articles }
</section>
);
}
});
module.exports = Articles;
Array.from() takes an iterable object to convert to an array and an optional map function. You could create an object with a .length property as follows:
return Array.from({length: this.props.level}, (item, index) =>
<span className="indent" key={index}></span>
);
I'm using Object.keys(chars).map(...) to loop in render
// chars = {a:true, b:false, ..., z:false}
render() {
return (
<div>
{chars && Object.keys(chars).map(function(char, idx) {
return <span key={idx}>{char}</span>;
}.bind(this))}
"Some text value"
</div>
);
}
You can still use map if you can afford to create a makeshift array:
{
new Array(this.props.level).fill(0).map((_, index) => (
<span className='indent' key={index}></span>
))
}
This works because new Array(n).fill(x) creates an array of size n filled with x, which can then aid map.
I think this is the easiest way to loop in react js
<ul>
{yourarray.map((item)=><li>{item}</li>)}
</ul>