React render array of objects in cells of Bootstrap grid - javascript

I have an array of components and I want to render each element in a different cell. The length of the array can be always different and can be more then 12 elements that are the limit of the cells in the Bootstrap grid system. So if the elements are more then 12 I need to create a new rows.
In my RENDER method I have this:
let components = this.state.elements.map(
(currElement, index) => this.renderElement(currElement, index)
);
return (
<div className="wrap-content container-fluid">
<section id="box-main" className="container-fluid">
<div className="row flex-items-xs-center">
<div className="col-xs">
<header>
<h4>{this._title}</h4>
</header>
</div>
</div>
{components}
</section>
</div>);
The method renderElement now render each element in a row, but I want to render in a cell but I don't know how to do. The ElementItem is a component that render the single element.
renderElement(element, index)
{
return(
<div key={index} className="row flex-items-xs-center">
<div className="col-xs">
<ElementItem element={element} propA={element.propA} template={2} />
</div>
</div>
);
}

Christian, I'm not sure if I understood your question. But if you want each element rendered in a column(cell) you must declare your components inside row and then specify the breakpoints. Something like this:
const MainComponent = props =>{
const elements = elements.map(element =>{
return(<div className="col md-4"> {element} </div>);
})
return(
<div className="row">
{elements}
</div>
)
}

Related

How do I make React onClick calls only affect a single item, instead of all?

I have an array of objects, and for each one I .map it into a component called Card.js. Each card has an 'edit' button, and I have an edit form which I want to appear ONLY for the card on which I clicked the button.
At the moment, whatever I try to do to pass an id into the Editform.js component, it still makes the form appear for all of the card components.
Here's the current component I call which is meant to render just form for the clicked button. I pass in all of the cards in the 'cards' array, and what I believe is the id of the current .map object from the calling function:
function Editform({ cards, setCards, id }) {
const thisCard = cards.filter((card) => card.id === id)[0];
const editThisCard = thisCard.id === id; // trying to match id of passed card to correct card in 'cards' array.
console.log(editThisCard);
return (
<>
{editThisCard && ( // should only render if editThisCard is true.
<div className="form">
<p>Name of game:</p>
<input type="text" value={thisCard.gamename}></input>
<p>Max players: </p>
<input type="text" value={thisCard.maxplayers}></input>
<p>Free spaces: </p>
<input type="text" value={thisCard.freespaces}></input>
<p>Table #: </p>
<input type="text" value={thisCard.tablenum}></input>
<p></p>
<button type="button" className="playbutton">
Save changes
</button>
</div>
)}
</>
);
}
export default Editform;
edit: apologies, I forgot to paste in the other code. Here it is. Note that I'm just hardcoding in a couple of cards for now:
import React from "react";
import ReactFitText from "react-fittext";
import Editform from "./Editform";
function Displaycards({ lastid }) {
const [cards, setCards] = React.useState([
{
id: 1,
gamename: "El Dorado",
maxplayers: 4,
freespaces: 1,
tablenum: 5,
},
{
id: 2,
gamename: "Ticket to Ride",
maxplayers: 4,
freespaces: 2,
tablenum: 3,
},
]); // using the React state for the cards array
const [showForm, setShowForm] = React.useState((false);
return (
<div className="cardwrapper">
{cards.map(({ id, gamename, maxplayers, freespaces, tablenum }) => {
return (
<div key={id}>
<div>
<div className="card">
<ReactFitText compressor={0.8}>
<div className="gamename">{gamename}</div>
</ReactFitText>
<div className="details">
<p>Setup for: </p>
<p className="bignumbers">{maxplayers}</p>
</div>
<div className="details">
<p>Spaces free:</p>
<p className="bignumbers">{freespaces}</p>
</div>
<div className="details">
<p>Table #</p>
<p className="bignumbers">{tablenum}</p>
</div>
<button type="button" className="playbutton">
I want to play
</button>
<br />
</div>
<div className="editbuttons">
<button
type="button"
className="editbutton"
onClick={() => setShowForm(!showForm)}
>
Edit
</button>
<button type="button" className="delbutton">
X
</button>
</div>
{showForm && (
<div>
<Editform
cards={cards}
setCards={setCards}
id={id}
/>
</div>
)}
</div>
</div>
);
})}
</div>
);
}
export default Displaycards;
I feel like I'm missing something obvious, but I can't get my head around what it is. The current iteration of it is here - https://github.com/TSDAdam/lfp/tree/usestate-trial - and it was created with create-react-app .
It sounds like you have one state controlling all of the Cards. You haven't shown the Card component yet however. Have every Card control its own state, so when the edit button bound to the card is clicked, it only applies to that one card. If you show us more code we can narrow it down, but this is most likely the gist of your problem.
The problem is that the EditForm is inside the map function, so for every item in your cards array, a separate EditForm is rendered with the corresponding values, and all these EditForms get shown/hidden based on the same boolean in your state.
The solution is to move the EditForm outside the map function, and create a new state object that tracks an "active" card, from where the single EditForm could take its values.
This of course won't work if you want to render the EditForm in a position relative to the "active" card.
[Edit]
Okay, I ended my answer with a caveat, but I should add a solution for that as well, since it isn't very complicated.
If you want to render an EditForm below the selected card, for example, the approach would be to keep it inside the map function as it is now, and change the boolean state variable showForm into one that accepts a string/number (depending on what you use as the identifier for each card). And then use this state variable to determine which form shows at any given time.
const [showForm, setShowForm] = React.useState("");
{cards.map(({ id, gamename, maxplayers, freespaces, tablenum }) => {
return (
<div key={id}>
// Rest of the JSX
<div className="editbuttons">
<button
type="button"
className="editbutton"
onClick={() => setShowForm(id)}
>
Edit
</button>
<button type="button" className="delbutton">
X
</button>
</div>
{showForm == id && (
<div>
<Editform
cards={cards}
setCards={setCards}
id={id}
/>
</div>
)}
</div>
</div>
);
})}

How to render a component outside the component that contains the function that renders the first component?

The situation is a bit complicated:
inside a component called "LeftSectionHeader" I have a div, which when clicked must render a component;
the component to be rendered is called "ProfileMenu", and is basically a div that must be rendered on top of "LeftSectionHeader" itself and another div;
All these components are rendered inside another component called "Main".
The problem is that if I define the function inside "LeftSectionHeader", "ProfileMenu" will be rendered inside, while I need it to not only be rendered outside, but even cover it; that's why you'll see some boolean vars inside "Main", because that is the only way i could render it, but it still doesn't cover the other divs. I'll attach the code of each component and how the final result should look below.
LeftSctionHeader:
function LeftSectionHeader(){
return(
<div class="left-section-header">
<div class="crop" ><img src="./images/profiles/anonimous.png" /></div>
</div>
);
}
The div belonging to the "crop" class is the one that must be clicked to render "ProfileMenu"
ProfileMenu:
function ProfileMenu(){
return(
<div class="profile-side-menu">
//A lot of boring stuff
</div>
);
}
There are some functions related to this component, but they are not important, so I didn't put them, just ignore it
Main:
var p=true;
var m=true;
function Main(){
return(
<div class="main">
<Header />
<div class="left-section">
{m ? <div><LeftSectionHeader /><LangMenu /></div> : <ProfileMenu />}
</div>
{p ? <PostPage /> : <NoPostsMessage />} //Ignore this line
</div>
);
}
Before clicking on the orange div
After clicking
This might help as guidline, hopefully!
function LeftSectionHeader({ onClick }){
return(
<div class="left-section-header" onClick={onClick}>
<div class="crop" ><img src="./images/profiles/anonimous.png" /></div>
</div>
);
}
function Main(){
const [showProfile, setShowProfile] = useState(false);
return(
<div class="main">
<Header />
<div class="left-section">
{!showProfile ? (
<div>
<LeftSectionHeader onClick={() => setShowProfile(true)} />
<LangMenu />
</div>
) : <ProfileMenu />}
</div>
{p ? <PostPage /> : <NoPostsMessage />} //Ignore this line
</div>
);
}
The simplest solution might be to pass a handler into the header component to toggle the menu:
function App () {
const [showMenu, setShowMenu] = useState();
return (
<div>
<Header onMenuToggle={() => setShowMenu(!showMenu)} />
{ showMenu && <Menu /> }
</div>
)
}
function Header ({ onMenuToggle }) {
<div onClick={onMenuToggle}>...</div>
}
Caveat: This will cause the entire App component to re-render when the menu state changes. You can mitigate this by either
A) placing the menu state closer to where it's actually needed, like in the sidebar component instead of at the top, or
B) using a context or other orthogonal state store.
Another approach would be to leave the state handling in the LeftSectionHeader component and then use a React portal to render the menu elsewhere in the DOM.

how to use props to change className ? (reactjs/css)

I don't succeed by defining a function
and call this function below by just changing className for each element ..
Here is the code:
const ProgressBar = (props)=>{
return(
<div className={props.styleName}>
<div className={classes.ProgressBarGradient}>
<div className={classes.GradientHider}/>
</div>
</div>
)
}
const GridContent = (props)=>{
return (
<div className={classes.Container}>
<h3 className={classes.Title1}>progress vs target</h3>
<h3 className={classes.Title2}>weekly goal</h3>
<h3 className={classes.Title3}>monthly goal</h3>
<ProgressBar styleName="ProgressBar1"></ProgressBar>
</div>
);
}
What I want is to change only the className of the first div when I call the function below.
Thank you so much :)
PS: I use css modules so when I call my class I usually do className={classes.className}
In your ProgressBar use this :
<div className={classes[props.styleName]}>

map() method on multi dimensional array in gatsby/react, getting error while trying to wrap inner loop in div

i want to display three posts per row in a gatsbyjs web page. i am having an array of posts, if i were to display using columns it would be just applying the multiline class on the posts container div. but i am using tiles layout built-in in bulma css, unfortunately that don't support multiline likes columns do.
so i have to wrap every three posts in a div with class 'tile'. for which i split the array into chunks of 3 size each (as suggested by someone here, thanks to him) now it boiled down to looping through a two dimensional array.
now the problem is i am getting syntax error when trying to wrap the inner loop in a div (i am new to javascript/es6 and react/gatsby, so i am hoping someone here would help me out) below is the code, i commented out the divs for which i am getting error. so how to wrap the inner loop in a div in gatsby/react. TIA
return(
<div className="tile is-ancestor">
{chunks.map((chunk)=> {
return (
//<div class="tile">
chunk.map((edge) => {
return(
<div className="tile is-parent is-4">
<div className={`tile is-child notification ${edge.node.frontmatter.type}`}>
<p className="is-7">{edge.node.frontmatter.date}</p>
<h2 className="subtitle is-5">{edge.node.frontmatter.title}</h2>
<p>{edge.node.excerpt}</p>
</div>
</div>
)
}
)
//</div>
)
})}
</div>
);
You would need to wrap the code inside that div in curly braces { } to be valid JSX. Just like you did with the first chunks.map.
return(
<div className="tile is-ancestor">
{chunks.map((chunk)=> {
return (
<div class="tile">
{
chunk.map((edge) => {
return(
<div className="tile is-parent is-4">
<div className={`tile is-child notification ${edge.node.frontmatter.type}`}>
<p className="is-7">{edge.node.frontmatter.date}</p>
<h2 className="subtitle is-5">{edge.node.frontmatter.title}</h2>
<p>{edge.node.excerpt}</p>
</div>
</div>
)
}
)
}
</div>
)
})}
</div>
);

passing props to another Component and being pushed to variable and being displayed at same time

I don't understand this method of passing props to a Class and at the same time pushing it into a variable.
class Grid extends React.Component{
render(){
const width=(this.props.cols*14);
var rowsarr=[];
var boxclass="";
for(var i=0;i<this.props.rows;i++){
for(var j=0;j<this.props.cols;j++){
let boxid=i+"_"+j;
boxclass=this.props.gridfull[i][j]? "box on":"box off";
rowsarr.push(
<Box
boxclass={boxclass}
key={boxid}
boxid={boxid}
row={i}
col={j}
selectbox={this.props.selectbox}
/>
);
}
}
return(
<div className="grid" style={{width:width}}>
{rowsarr}
</div>
);
}
}
In the above code, I see that the props have been passed to the box component but at same it is pushed to rowsarr and rowsarr is being returned.I need an explanation as what is being done here and how this technique works in reactjs.
i guess it is used to display a list of boxes <Box>.
But is is very poorly written.
The common pattern is to iterate on this.props.cols
this.props.cols.map(col => <Box> ...)
What this code is trying to do is form a grid layout or simply, a list of rows (i) with columns (j).
For every item (Box) - row i and column j, the item is inserted into an array, rowsarr and different props are passed to it such as className etc.
Once the rows and columns are filled in the rowsarr array it is passed as the child to <div className="grid" style={{width:width}}> so that <div className="grid"> could render the rowsarr elements as its child.
That return method it will return a JSX element which is a div with multiple elements. Let's deep into it:
return(
<div className="grid" style={{width:width}}>
{rowsarr}
</div>
);
The above code it's similar to:
return(
<div className="grid" style={{width:width}}>
{[ <Box />
<Box />
<Box />
<Box />
<Box />
]}
</div>
);
Each of this Box class will have different attributes that are calculated based on the props received by Grid Component.
Each box will have different attributes. And each box is put into an array which is put inside of that div. I just asume that Box is a class that returns a div element, so the above code will be rendered as:
<div className='grid' style={{width:width}}>
<div className='box'></div>
<div className='box'></div>
<div className='box'></div>
<div className='box'></div>
<div className='box'></div>
</div>
This code creates an array of <Box> components and renders them inside a <div>. The class name and implementation do not match. This code creates a single row, not an entire grid. There is a mistake in the logic.

Categories