I have 6 buttons that I'd like to render on my web page.
I'm mapping through an object of arrays in a JSON file through map()
template(x) {
return (
<div>
<Button bsStyle="primary"></Button>
</div>
)
}
render() {
return (
<div className="row">
{_(this.props.data).map((x) => this.template(x))}
</div>
)
}
How can I limit each line to only have 3 buttons, instead of having however many it takes until it's forced to go down to the next line? I thought of just splitting up my the object arrays in the JSON file to two separate object arrays, so I can just make two map() calls? There seems to be a better and easier solution.
Split the whole array into chunks (smaller arrays each of which contains only 3 elements) and push those arrays into arrayOfChunks (array of arrays) then, in the render function:
{arrayOfChunks.map(this.templateChunk)}
and the method:
templateChunk(chunk) {
return (<div className="row">
chunk.map(this.template)
</div>)
}
My React knowledge is limited so my syntax is probably off but what this does in the render function is return an array of row divs each containing 3 buttons. I think an easier solution is using flex-box with your css which would allow you to specify how many elements you want on row.
render() {
return this.props.data.reduce((prev, curr, ind, arr) => {
if (ind + 1 % 3 === 0) {
prev.push(createRow(arr.splice(ind - 2, ind + 1)));
}
return prev;
}, [])
}
createRow(arr) {
return (
<div className="row">
{arr.map((x) => this.template(x))}
</div>
)
}
template(x) {
return (
<div>
<Button bsStyle="primary"></Button>
</div>
)
}
Related
I have a gallery that displays a number of books per row. This gallery takes an array of books as a prop and uses "itemsPerRow" prop to chunk the books into a 2 dimensional array and then loops through all the books to display the books in a grid-like structure.
export default function Gallery({ items, itemsPerRow, renderLink }) {
itemsPerRow = itemsPerRow ?? 3
const rows = chunk(items, itemsPerRow)
const renderEmptyItems = (itemsToRender) => {
const items = []
for(let n = itemsToRender; n > 0; n--) {
items.push(<GalleryItem key={`empty_${n}`} empty/>)
}
return items
}
return (
<div>
{
rows.map((row, index) => (
<div key={index} className="tile is-ancestor">
{row.map(item => <GalleryItem key={item.id} renderLink={renderLink} {...item}/>)}
{/* Add empty gallery items to make rows even */}
{index + 1 === rows.length && renderEmptyItems(itemsPerRow - row.length)}
</div>
))
}
</div>
)
}
However, unless I give each div representing a row a key, react complains about the lack of keys. As I understand it, using the index as a key doesn't really help react and should be avoided. So what should I use as a key here <div key={index} className="tile is-ancestor"> instead of the index?
Use a unique identifier (book.id, maybe book.title if it's unique) for the key props. If your data does not have a unique identifier, it's okay to use index.
You need to specify a value that uniquely identify the item, such as the id. You can read more about keys in the documentation.
Also it is not recommended to use indexes as keys if the order of your data can change, as React relies on the keys to know which components to re-render, the documentation I linked explains that further.
You can use the unique_identifier which differentiate each of the documents(probably, you should pass the document _id as a key prop in the row components)
<div className="row">
{notes.map((item) => {
return (
<div key={note._id} className="col-md-6">
<Component item={item} />
</div>
);
})}
</div>
I am working with bigger arrays in React, and want the following display like this: image/name image/name image/name. I have the following the code but I don't know how I can map over the images array to so it shows it's image. Thank you
function showProtocolsNames() {
if (supportedVaults) {
let arr = supportedVaults
.map((item) => item.protocolName)
.filter((item, index, arr) => {
return arr.indexOf(item) == index;
});
let arrImages = supportedVaults
.map((item) => item.protocolKey)
.filter((item, index, arr) => {
return arr.indexOf(item) == index;
});
let protocolsName = [...new Set(arr)];
let protocolsImages = [...new Set(arrImages)];
console.log(protocolsName, protocolsImages);
return protocolsName.map((vault) => {
return (
<>
{' '}
<img
src={getVaultIcon(vault)}
width="42px"
height="42px"
style={{
marginRight: '12px',
}}
/>
<p className="vaults-protocol">{vault}</p>
</>
);
});
}
return null;
}
Solved: By creating an array of the images and names together and just mapping over it like DBS suggested in comments.
I believe there is a much simpler solution to your problem than your current approach. For example, you could use the supportedVaults data immediately while mapping the image/name components, like this:
function showProtocolsNames() {
// added check to ensure there is data inside supportedVaults
if (supportedVaults.length) {
// removed the two mapped arrays
// added index which is generated by map function
return protocolsName.map((vault, index) => {
// added div instead of <> in order to include a key, which is required in a map function
return (
<div key={`${index}-${vault?.protocolKey}`}>
{" "}
<img
src={getVaultIcon(vault?.protocolKey)} // here we pass protocolKey to the getVaultIcon function
width="42px"
height="42px"
style={{
marginRight: "12px",
}}
/>
{/* here we add protocolName inside the paragraph */}
<p className="vaults-protocol">{vault?.protocolName}</p>
</div>
);
});
}
return null;
}
This logic above is based on your description of the issue, assuming protocolKey is what you need to pass to get the vault icon in getVaultIcon function and protocolName is the value you need to show as the name. If my perception is wrong, please edit your question to reflect more info on what exact data you need to get from the supportedVaults array, or what format supportedVaults has.
I have a map loop that returns a list of values eg 0 - 100. The problem is that this will return the whole list. I want to only return a specific number of values. eg (11-20). What would be the best way of implementing this?
The current code is
const all = this.props.memories.map((value, index) => {
return (
<div>
{value}
</div>
)
})
Use .slice() as this.props.memories.slice(11, 20).map...
Slice ..Use it when you do not want to alter the original contents, instead a shallow copy of that for local change and usage.
this.props.memories.slice(11,20).map((value, index) => {
return (
<div>{value}</div>
)
})
I have a react component that I need to render a bunch of elements based on the items in an array. I've setup a function to render over all of them in the array and it works nicely. Now I want to split the rendered elements into two different wrapper elements. I tried a few ways of doing this using if statements and switches but my code was getting really messy. I wanted to see if there is a cleaner way to do this, that someone might know.
renderSplit(key) {
return <div className={`inner_${key}`} key={key}>{`inner_${key}`}</div>;
}
render() {
const { arr } = this.props;
return (
<div>
{arr.map(this.renderSplit)}
</div>
);
}
So if I have an array of 5 items I want the first 4 that get rendered by renderSplit to be wrapped in a container
<div className='left'>.....</div> and the last item in the array to be wrapped in another wrapper <div className='right'>....</div>
If there is only one item, in the array it doesn't get wrapped by any wrapper div.
It should be arr.map not body.map next comes pass in the array . Then invoke the function
do this:
renderSplit(arr) {
return arr.map((entry,index)=>
<div className={`inner_${entry}`} key={entry}>{`inner_${entry}`}</div>;
)
}
render() {
const { arr } = this.props;
return (
<div>
{this.renderSplit(arr)}
</div>
);
}
you can input a condition(with respect to the index) inside the map statement , to satisfy the last statement
you can use destructuring assignment to extract the first elements and the last one from the array, check if the last one exists, if it doesn't, that means the array has only one entry, and render accordingly :
renderSplit(key) {
return <div className={`inner_${key}`} key={key}>{`inner_${key}`}</div>;
}
render() {
const { arr } = this.props;
const [last, ...firstElems ] = arr.reverse(); // reverse the array to get the last element and use the spread operator to get the rest.
return (
firstElems.length > 0 // if you have elements in the firstElems array
? (
<div className="wrapper">
<div className="left">{firstElems.reverse().map(this.renderSplit)}</div> // reverse the firstElems array to display it in the right order.
<div className="right">{this.renderSplit(last)}</div> // render the last element.
</div>
)
// other wise, render the single element you have.
: <div className="singleElement">{this.renderSplit(last)}</div>
);
}
Let's see:
renderSplit(key) {
return <div className={`inner_${key}`} key={key}>{`inner_${key}`}</div>;
}
render() {
const { arr } = this.props;
if(!arr || !arr.length) { return null; }
if(arr.length === 1) { return renderSplit(arr[0]); }
const left = arr.slice(0, arr.length - 2);
const right = arr[arr.length -1];
// Or else:
// const rightElement = arr.pop(); const leftElements = arr;
return (
// or <React.Fragment>
<>
<div className="left">
{leftElements.map(this.renderSplit)}
</div>
<div className="right">
{this.renderSplit(rightElement)}
</div>
</>
);
}
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>