Reactjs have <div> wrapper the elements on every 4th elements - reactjs

React group 4 items in a div with class row. At the moment you can see below I have a <div> group around the articles.
// articles is array of article object
<div className="row">
<section className="section--6-or-4-items section-featured campaign-teasers">
{articles.map(item => <Teaser {...item} key={item.id} />)}
</section>
</div>
Should I create a new component which have <div> wrapper around and only accept 4 arrays at a time to render out the articles.

Your best bet here is to build your array of articles into an array of 4-length arrays. You can do this using lodash's handy chunk method.
Once you've chunked your articles, you can iterate over them in your JSX, something like this:
const sections = chunk(articles, 4);
return (
<div className="row">
{sections.map((articles, i) => (
<section className="..." key={i}>
{articles.map(item => <Teaser {...item} key={item.id} />)}
</section>
)}
</div>
);
Edit: If you don't want to use lodash, you can use reduce to great effect:
function chunk(array, size) {
return array.reduce((chunks, item, i) => {
if (i % size === 0) {
chunks.push([item]);
} else {
chunks[chunks.length - 1].push(item);
}
return chunks;
}, []);
}

Personally, I extract rendering to dedicated methods.
<div className="row">
<section className="section--6-or-4-items section-featured campaign-teasers">
{ this.renderArticles() }
</section>
</div>
Renderer method here...
renderArticles() {
return createChunks(articles, 4).map((chunk, idx) => (
<div key={idx}>
{ chunk.map((props, idx) => <Teaser key={idx} {...props} />) }
</div>
));
},
And finally, function which chunks array into smaller array on N size
const createChunks = (array, size) => {
const copy = array.concat();
const chunks = [];
while (copy.length) {
chunks.push( copy.splice(0, size) );
}
return chunks;
}

Related

Component is not being returned in nested loop

I am trying to render the child component inside a nested loop. However it is not being render in the second loop(red tick). Although it is rendered normally in the first loop (blue tick). Kindly highlight why is it no rendered in the second loop.
https://i.stack.imgur.com/LFiKU.png
Codesandbox Link : https://codesandbox.io/s/competent-nova-u9rzuh?file=/src/parent.js
import React from "react";
import ProductFeaturesCards from "./ProductFeaturesCards.js";
import { Products } from "../ProductsData.js";
const ProductFeatures = ({ className = "", id }) => {
return (
<section id='product-features' className={`${className}`}>
<div className='container'>
<div className='row d-flex justify-content-center'>
<div className='col-lg-12 col-md-12 col-sm-12 col-12 py70'>
<p className='features-title'>Product Features</p>
</div>
</div>
<div className='row'>
{Products.forEach((item, i) => {
if (item.id === id) {
// return <ProductFeaturesCards data={item} key={i} />;
Object.values(item.Product_features[0]).map((feature, index) => {
console.log("ProductFeaturesCards:", feature);
return <ProductFeaturesCards data={feature} key={index} />;
});
}
})}
</div>
</div>
</section>
);
};
export default ProductFeatures;
Can you try this. Not sure if this will work
if (item.id === id) {
// return <ProductFeaturesCards data={item} key={i} />;
return Object.values(item.Product_features[0]).map((feature, index) => {
console.log("ProductFeaturesCards:", feature);
return <ProductFeaturesCards data={feature} key={index} />;
});
}
The first mistake you did was using forEach. forEach will mutate the data and will not return anything. So, Instead you need to use map which doesn't mutate and returns the result.
The 2nd mistake is the return statement not added inside the if condition for the map. So, it never gets returned and hence your first map will not receive the value.
After this you should be able to run it.
<div className="row">
{Products.map((item) => { // replaced forEach with map
if (item.id === id) {
return Object.values(item.Product_features[0]).map( // return added
(feature, index) => {
return <Card data={feature} key={index} />;
}
);
}
})}
</div>

Whats the best way to make use of a counter variable in this nested 'map' loop so that each square key or value incremenents up from 1 to 9

return (
<div>
<div className="board">
{
[1,2,3].map( (row) => {
return <div key={row} className="board-row">
{
[1,2,3].map( (col) => {
return <Square key={col}></Square>
})
}
</div>
})
}
</div>
</div>
);
How might i make it so the Squares generated by this nested for loop (using array map functions) could use a counter so that the square's value would be 0 to 8 or 1 to 9.
I am struggling to create a variable outside of these javascript arrow functions...
(I have a Java background and am struggling i little wrapping my head around javascript/react :) )
Many thanks for any help/direction.
John
EDIT:
Below is solution that worked for me in the end!
{
[1,2,3].map( (row) => {
return <div key={row} className="board-row">
{
[1,2,3].map( (col) => {
return this.renderSquare(++count);
})
}
</div>
})
}
</div>
Here is renderSquare function also:
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
key = {i}
/>
);
}
I think a lot of the confusion is because of JSX and learning how and when you can type HTML and/or JavaScript where.
FINAL EDIT:
please note, i changed from ++count to count++ to avoid the initial increment occurring too early. Doing this breaks the calculateWinner logic, just if anyone else is doing the tic tac toe tutorial! :)
If you just want to count and output the number of times the second loop is called then the following is a solution to it:
import React from 'react';
export default function Test2() {
let count = 0;
return (
<div>
<h1>Test 1 Component</h1>
{[1, 2, 3].map((row) => {
return (
<div key={row}>
{[1, 2, 3].map((col) => {
return <div key={col}>{++count}</div>;
})}
</div>
);
})}
</div>
);
}

How to render component with for loops in react

I'm trying to create a carousel in react. I have two states, the first and last index of the carousel itself. The states is used to determine how many products are displayed in the carousel. Both are initialized as the number 0 and 4, respectively. The code for displaying the products are as follows:
const displayCarouselItems = () => {
for (let i = firstCarouselIndex; i < lastCarouselIndex; i++) {
return <ProductCard product={products[i]} />;
}
};
and here is how I called the function in my JSX:
return (
<div className="container flex justify-between relative">
<button onClick={() => nextButtonHandler()}></button>
{displayCarouselItems()}
<button onClick={() => prevButtonHandler()}></button>
</div>
);
It only renders one product card in my page. Would anyone know why the for loop doesn't iterate?
It only shows one component because once you return from the displayCarouselItems function, it's over, no more looping
You might want to return an array of elements, like this:
const displayCarouselItems = () => {
const renderValue = []
for (let i = firstCarouselIndex; i < lastCarouselIndex; i++) {
renderValue.push(<ProductCard product={products[i]} />)
}
return renderValue
};
Then it would be better with a key
// Use index as key
renderValue.push(<ProductCard product={products[i]} key={i} />)
// Or even better, an id, which avoid issues with rendering
// if the sequence of the items change
renderValue.push(<ProductCard product={products[i]} key={products[i].id} />)
That said, I think a more declarative pattern would be to use map over your products collection, for example in a pure function like the following:
const renderCarouselItems = products =>
products.map(p => (
<ProductCard key={p.id} product={p} />
));
Which, can as well be used directly in the markup unless you need it somewhere else:
return (
<div className="container flex justify-between relative">
<button onClick={() => nextButtonHandler()}></button>
{products.map(p => (
<ProductCard key={p.id} product={p} />
))}
<button onClick={() => prevButtonHandler()}></button>
</div>

How to render the array of arrays using React?

I have an array of string arrays like,
items : [a[],b[],c[],d[]].
I want to render the elements of this array as a carousel using React. The following code doesn't work. Any help would be appreciated.
public render(): React.ReactElement<IReactCarouselProps> {
return (
<div>
<Carousel>
{this.state.items.map((list,key) => {
return(
<div key = 'key'>
<div>{list.a}</div>
</div>
);
}
)}
</Carousel>
</div>
);
}
working example https://jsfiddle.net/shuts13/cfLxrn15/6/
render() {
return (
<div>
{this.state.items.map((subArray, index) => {
return (
<div key={index}>
{subArray.map((subitem, i) => {
return (
<ul>
<li>{subitem.value}</li>
</ul>
);
})}
</div>
);
})}
</div>
);
}
To render a array of arrays you can map the second array too. This way your code will loop the parent array and loop all the child arrays, rendering them the way you want.
Here is a example:
public render(): React.ReactElement {
const flatArray = parentArray.map(
(childArray) => {(childArray.map((childElement) => <div>Your code here</div>)})
);
return (
{flatArray}
);
}
or, even better, you can use array flat.function(), like this:
public render(): React.ReactElement {
const flatArray = parentArray.flat().map((childElement) => <div>Your code here</div>)
return (
{flatArray}
);
}
Read more about array.flat() method here: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Array/flat

Mapping array to JSX elements inside rows

Using React I am trying to map an array of elements into a containing HTML element.
However I can not figure out how to map two elements into one HTML element since these elements will have to be closed.
This is an example of what I would like to do. Map two components inside a containing element of className="row" but I can not because the JSX elements have to be closed:
const CalendarGrid = (events) => {
let content = [];
events.events.map((event, index) =>{
if (index % 2 != 0) {
content.push(
<div className="row">
<EventCardRight key={event.id} event={event} align="right"/>
)
}if (index % 2 == 0)
{
content.push(
<EventCardLeft key={event.id} event={event} />
</div className="row">
);
}
});
return (
<div>
{content}
</div>
);
}
You can take advantage of inline logical operators and map through events directly as:
const CalendarGrid = (events) => {
return (
<div>
{events.events.map((event, index) =>
<div key={event.id} className="row">
{index % 2 != 0 ? (
<EventCardRight key={event.id} event={event} align="right"/>
) : (
<EventCardLeft key={event.id} event={event} />
)}
</div>
)}
</div>
)
}
First, I would split your array to chunks of size 2. See How to split a long array into smaller arrays, with JavaScript, I will use lodash for that:
const rows = _.chunk(events.events, 2);
now I can simply map every row to elements:
const content = rows.map((rowEvents, index) => (
<div key={index} className="row">
<EventCardRight event={rowEvents[0]} align="right" />
<EventCardLeft event={rowEvents[1]} />
</div>
));

Resources