React/Gatsby: Conditionally wrap every two posts inside a div - reactjs

I'm trying to wrap every two posts inside a container div. Below is what I tried, but unfortunately I get an error.
This is what I tried:
I have a variable called postIndex. While the posts are being iterated over with posts.map(({ node }) => {...}, I have a conditional if/else check to see if postIndex is odd or even with if (postIndex % 2 == 0) (check if postIndex is even) and if (postIndex % 2 == 1) (check if postIndex is odd).
If postIndex is even I render out only the opening <div> tag that is the container for the two posts. If postIndex is odd, then I render out only the closing </div> tag.
I get an error with this implementation, though. What is the right way to go about doing something like this?
Example of what I tried:
let postIndex = 0
return (
<Layout>
{posts.map(({ node }) => {
if (postIndex % 2 == 0) {
postIndex++
return (
<div>
<p>test</p>
)
} else if(postIndex % 2 == 1) {
postIndex++
return (
<p>Test</p>
</div>
)
}
})
}
</Layout>
)

An opening tag without a closing tag is invalid JSX. You can probably do something like this below though. Also, you have access to the index of the array in a map, so you don't need to create a new variable.
return (
<Layout>
{posts.map(({ node }, index) => {
if (index % 2 === 0) {
return (
<div key={index}>
<p>{node}</p>
{posts[index + 1] && <p>{posts[index + 1].node}</p>}
</div>
)
}
})
}
</Layout>
)

Related

React Loop Through FileList and Display

I am trying to loop through a FileList in React and not having any luck.
I have read this article on Can't use forEach with Filelist and with it's help I am able to print to the console. And this Loop inside React JSX article to help with the loop part however I am not able to display any results.
renderEligibilityDocs(e) {
var proctorDocChanges = this.state.proctorDocChanges;
var files = proctorDocChanges[313];
console.log("files", files);
if (files) {
var post = Array.from(files).forEach(file => {
return (
<div key={file.name}>
<h2>file: {file.name}</h2>
</div>
);
});
Array.from(files).forEach(file => console.log("Print to Console " + file.name));
return <div>
{post}
</div>;
} else {
return <div>
<span>No Files Uploaded</span>
</div>;
}
}
What is the concept that I am missing to display the files in the H tag?
If you want to capture or render the output you should use map instead of forEach.
forEach executes a function for each element but it doesn't do anything with the return values, whereas map builds an array from them.
if (files) {
return Array.from(files).map(file => {
return (
<div key={file.name}>
<h2>file: {file.name}</h2>
</div>
);
});
}
else {
...
}
The forEach method doesn't return anything. This is fine for your second loop where you just want to do a console.log, but your first loop needs to return something - you should use map there.
You can also move the map statement into the return statement:
if (files) {
return (
<div>
{Array.from(files).map(f => (
<h2 key={f.name}>file: {f.name}</h2>
)}
</div>
)
}

How to display a component stored in a variable in React

I have a block of code where I need to display a badge when the passport is due to expire, otherwise no badge should be displayed:
const displayMonths = (passport_date) => {
const months = moment().diff(passport_date, 'months') * (-1)
if (months > 0 && months <= 12) {
return <span class="badge badge-warning"> months remaining</span>
}
else if (months <= 0) {
return <span class="badge badge-danger"> months expired</span>
}
else {
return ""
}
}
return (
<>
{`${moment(row.passport_date).format('DD/MM/YYYY')} ${displayMonths(row.passport_date)}`}
</>
)
The problem is that inside of the return the displayMonths is displaying the object (Eg: 01/04/2018 [object Object]) and I cannot display the related HTML instead. How to achieve this? Thanks
You return should be like below. Because you are returning a jsx. Putting that into a `` would try to convert it to a string and hence [object object]. The below way would render the jsx directly.
return (
<>
{moment(row.passport_date).format('DD/MM/YYYY')}
{displayMonths(row.passport_date)}
</>
);

How to use conditional to generate element on the page

for (var k = 0; k < 10; k++) {
if (k % 2 === 0) {
weatherText = <div className="in_break">
}
weatherText += <div className="eachD" key={k}>
<div>
{
countIt === 0 ? (currDate.getHours() > 12 ? "Tonight" : "Today") : dayOfWeek[weekDay]
}
</div>
<div>
{
getDate
}
</div>
<div>
{
<ReturnIcon />
}
</div>
</div>
if (k % 2 === 0) {
weatherText += </div>
}
}
What I am looking to do is group all the eachD by two inside the `in_break' div
But I keep getting:
Parsing error: Unexpected token 'weatherText = </div>'
This is the layout:
in_break
eachD
eachD
in_break
eachD
eachD
in_break
eachD
eachD
...
Please help me resolve my issue
UPDATED
I hope this find it's way to your demand:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
renderInBreak = () => {
const items = this.setWeatherTextItems();
const inBreakItems = [];
let breakBlock = [];
let newBreak = false;
items.forEach((textItem, index) => { //1
if(!newBreak) {
breakBlock.push(textItem);
if(index + 1 === items.length){
inBreakItems.push(breakBlock);
}
} else {
inBreakItems.push(breakBlock);
breakBlock = [];
breakBlock.push(textItem);
//without this condition check, the last element will be left out of an odd array length
if(index + 1 === items.length) {
inBreakItems.push(breakBlock)
}
}
if(index % 2) newBreak = true; //false
else newBreak = false; //false
});
return inBreakItems.map(twoTextWeatherItems => (
<div className="in_break">
{twoTextWeatherItems}
</div>
))
}
render(){
<div>
{this.renderInBreak()}
</div>
}
OLD
React is supposed to handle things differently, maybe this will work:
Define a method in your component that will set your items:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
in your render method, or where you are willing to render these items:
render(){
<div className="in_break">{this.setWeatherTextItems()}</div>
}
Read more about how to render things in a loop.
You can add the conditions you want inside the for loop, or where it makes sense to you.
Not sure if the logic would work in a react environment but as far as I can see from your plain code when you are going to add the 'in_break' div aren't you just assigning the whole whetherText again instead of joining text to it?
Shouldn't this:
if (k % 2 === 0) {
weatherText = </div>
}
be written like this?
if (k % 2 === 0) {
weatherText += </div>
}
Edit following the typo correction:
I tried to run your code on codepen to have a quicker and easier understanding on how to find a solution.
I created an helper function with your code then I returned
<div className="Container" dangerouslySetInnerHTML={{__html: weatherText}}></div>
This enables you to have the result you are looking for. Only the even elements have the 'in_break' class.
Hope this helped and let me know if this is not correct.
Codepen: https://codepen.io/dpgian/pen/EBzRmX

Rendering a table where each cell correspond to an element with material-ui in React.js

So I need to make a table where each product corresponds to a cell; however, there's something with the JSX that prevents me from returning jsx when I don't close the tags in the returned JSX. I am not sure if there are alternate methods, but it seems that I am in a kind of bind, because the error message doesn't make sense and I get things like unexpected token.
Tried to return the JSX with map inside the callback, but it seems I can't do exactly what I want.
{props.products.slice(0, 50).map((element, i) => {
console.log(element.name);
if (i % 5 == 0) {
return (
<TableRow>
<TableRowColumn>{element.name}</TableRowColumn>
)
} else if (i % 5 == 4) {
return (
<TableRowColumn>{element.name}</TableRowColumn>
</TableRow>
)
} else {
return (
<TableRowColumn>{element.name}</TableRowColumn>
)
}
})}
I expect each row to have 5 columns, and each cell containing a product and the table containing 50 elements. Basically, I want to close the row after 5 column.
I would like to have something like a 5 by 5 table in the end or 5 by 10 to be exact.
You can check some examples on material-ui site (https://material-ui.com/demos/tables/)
If you know which elements are headers names you can use for-loop over products to create columns name e.g.
<TableRow>
{props.products.map((product, index) => {
if (index % 5 === 0) {
return <TableCell>{product.name}</TableCell>;
}
}
</TableRow>
JSX is not HTML and you cannot brake up tag pairs.
Proper solution should be like this
function chunker(array, length) {
// select proper implementation on the link below
return []
}
{chunker(props.products.slice(0, 50)), 5).map((chunk) => {
if (chunk.length !== 5) {
console.warn('not full chunk', chunk)
}
return (
<TableRow>
{chunk.map((element, i) => {
console.log(element.name);
return (
<TableRowColumn>{element.name}</TableRowColumn>
)
})}
</TableRow>
)
})}
Select chunk implementation here Split array into chunks

How to write the complicated rendering loop in React?

I am writing a nested loop in React. All I am seeing is the final return statements of tags. Where are the and going? Thank you.
{ this.state.data.headings.map( (heading, i) =>
<h3 key={i}>{heading}</h3> &&
// some headings do not have subheadings, tho
// they still have statements. deal with these cases first...
((this.state.data.subheadings[i].length === 0 &&
this.state.data.statements[i].map((statement, _j) =>
<p key={i+_j}>{statement}</p>)) ||
// cases where the group of statements has a subheading...
(this.state.data.subheadings[i].map((subheading, j) =>
<h4 key={i + j}>{subheading}</h4> &&
this.state.data.statements[i][j].map((statement, k) =>
<p key={i+j+k}>{statement}</p>))
)
)
)
}
A better way of doing this in my opinion is to separate this in different components each one of them taking care of one of the loops.in your case header,subheader,statement, etc.
There is everything ok with you code, except you can refactor it to make more readable.
Don't repeat yourself (DRY), always move duplicated code to separate component, in your example it is statement element. Also, i remove redundant key props.
render() {
const {headings, subheadings, statements} = this.state;
return headings.map((heading, i) =>
<div key={i}>
<h3>{heading}</h3>
{
subheadings[i].length
? subheadings[i].map((subheading, j) =>
<div key={j}>
<h4>{subheading}</h4>
<Statements statements={statements[i][j]}/>
</div>
)
: <Statements statements={statements[i]}/>
}
</div>
);
}
const Statements = ({statements}) => (
statements.map((statement, i) =>
<p key={i}>{statement}</p>
)
);
(omg folks,) feels like i had to take a picture to prove it...
solution, special thanks to a similar Q&A (I'm using React v15 out of an older template for Ether dApps)
{ headings.map( (heading, i) =>
[ <h3 key={i}>{heading}</h3>,
subheadings[i].length === 0 ?
statements[i][0].map( (statement, j) =>
<p key={j}>{statement}</p>,
) :
subheadings[i].map( (subheading, j) => (
[<h4 key={j}>{subheading}</h4>,
statements[i][j].map( (statement, k) =>
<p key={k} style={{color: 'green'}}>{statement}</p> )
]
))
])
}

Resources