Bootstrap React Modal Component not working dynamically - javascript

I am using React Bootstrap, I am using map function of javascript to loop through admins, when i loop through them all values outside the modal display the correct values from the admins array, but inside the modal it shows only one standard object from the array everytime. The id of that component is different outside the modal and inside the modal, the modal inside displays only the user with id 1 in every component or every time the modal is clicked
admins = [ {id: 5, email: "angelinsneha#gmail.com", name: "angelin", role: "admin"},
{id: 4, email: "angelinsneha14#gmail.com", name: "angu", role: "admin"},
{id: 1, email: "angelin#gmail.com", name: "aanjuu", role: "admin"}]
<div className="rgtss pb-5">
{admins.length > 0
? admins.map((i) => (
<div className="bdr">
<div>
<p>{i.name} {i.id}</p>
<span>{i.email}</span>
</div>
<div className="mt-3">
<p>{i.role}</p>
</div>
<div>
<DropdownButton
id="dropdown-basic-button"
variant="dark"
title=""
size="sm"
>
<Dropdown.Item href={id == i.id ? "/profile" : ""}>
Edit
</Dropdown.Item>
{4 == i.id ? (
""
) : (
<Dropdown.Item onClick={handleShow}>Delete {i.id}</Dropdown.Item>
)}
</DropdownButton>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Delete Account {i.id}</Modal.Title> // only id 1 is displayed in modal, everytime the loop runs
</Modal.Header>
<Modal.Body>
Are you sure you want to delete this team account:{" "}
<b>{i.name}</b>?
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button
variant="danger"
onClick={() => handledelete(i.id)}
>
Delete
</Button>
</Modal.Footer>
</Modal>
</div>
</div>
))
: ""}
</div>
I have no idea why this is happening, can you'll help me?

You're creating modals in a loop (in this case, 3), which is not best practice for this very problem. You'd expect that each modal has the right props depending on the item of the array, but then how does the code know which modal to display ?
Your <Modal> should be outside the loop. Store a variable to know which admin to display when the modal is brought up. (It could be the index in the array, or adding a selected boolean to the admin).
When you trigger your handleShow, set this variable as well (don't forget to unset it when you're done, to prevent side-effects)
You should also use === instead of == unless it's really intended (see this SO post for more info)
EDIT: In code, it should look like this (don't copy/paste, it is untested)
const MyComponent = () => {
const [adminSelected, setAdminSelected] = useState(-1);
return (
<div className="rgtss pb-5">
{admins.length > 0
? admins.map((i, idx) => (
<div className="bdr">
<div>
<p>
{i.name} {i.id}
</p>
<span>{i.email}</span>
</div>
<div className="mt-3">
<p>{i.role}</p>
</div>
<div>
<DropdownButton
id="dropdown-basic-button"
variant="dark"
title=""
size="sm"
>
<Dropdown.Item href={id === i.id ? '/profile' : ''}>
Edit
</Dropdown.Item>
{4 === i.id ? (
''
) : (
<Dropdown.Item onClick={() => {
setAdminSelected(idx);
handleShow();}}>
Delete {i.id}
</Dropdown.Item>
)}
</DropdownButton>
</div>
</div>
))
: ''}
<Modal show={show} onHide={() => {
setAdminSelected(-1);
handleClose();}}>
<Modal.Header closeButton>
<Modal.Title>Delete Account {i.id}</Modal.Title> // only id 1 is
displayed in modal, everytime the loop runs
</Modal.Header>
<Modal.Body>
Are you sure you want to delete this team account: <b>{i.name}</b>?
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="danger" onClick={() => handledelete(i.id)}>
Delete
</Button>
</Modal.Footer>
</Modal>
</div>
);
};
Note that I added the idx variable in mapping to know which admin has been clicked (-1 for none), and I'm setting it along with the handleShow method.

Related

Add popup in every item of flatlist in React

I would like to add a popup form in each item in the Flatlist, and the item is actually a card.
My problem is that every time, I clicked the button, the popup shows but, when my mouse moves out of the card, instead of showing the original one, it shows another popup whose parent is the whole page.
I know the problem should be that I need not set the variable setPopup properly. But I don't know how to fix it.
When my mouse is on the card:
enter image description here
When my mouse is out of the card, the popup will move up and its position will be based on the whole page:
enter image description here
Thank you!
This is my code.
const [buttonPopUp, setButtonPopUp] = useState(undefined);
const renderItem = (item, index) => {
return(
<Card key={index} style={{width: '20rem'}}>
<CardImg className='galleryPics' top src={galleryPic.img} alt="..."/>
<CardBody>
<PopUpEditGallery
gallery={item}
index = {index}
trigger={buttonPopUp}
setTrigger={setButtonPopUp}
>
Edit
</PopUpEditGallery>
<CardTitle className='cardTitle'>{item.title}</CardTitle>
<CardText className='cardText'>{item.description}</CardText>
<Button className="btn-round btn-icon" color="primary" size='sm' onClick={()=> setButtonPopUp(index)}>Edit</Button>
</CardBody>
</Card>
);
}
return (
<div>
<div>
<Header/>
</div>
<div className="container">
<div className="row">
<div className='col-7'>
<ul>
<FlatList
list={values.gallery}
renderItem={renderItem}/>
</ul>
</div>
</div>
</div>
</div>
)
code for popup
return (props.trigger != undefined) ? (
props.trigger == props.index &&
<div className='popup'>
<div className='popupInner'>
<form onSubmit={handleSubmit(onSubmit)}>
<FormGroup>
<Label>Title</Label>
<Input
type="text"
placeholder={prev.title}
onChange={val => props.setTitle(val.target.value, prev.idx)}
/>
</FormGroup>
<Button className="btn-round btn-icon" color="primary" size='sm'>
Submit
</Button>
<Button className="btn-round btn-icon" color="default" size='sm'>
Cancel
</Button>
</form>
</div>
</div>
): "";

How to pass a value to a modal

For example, i have this functon that returns to me some ids and i get delete this posts with the id's returned with: onClick={() => { Delete(idvalue[i]) }}
However I need to open a modal before deleting the item asking if the person is sure they want to delete, and when they click on the yes I have no idea how to pass the right value to delete and where will stay this onClick to open the modal.
function Idchecker() {
for (let i = 0; i < idvalue.length; i++) {
if (idvalue[i] == item.id) {
return (
<div className="img-array">
<button className="click-img" onClick={() => { Delete(idvalue[i]) }}>
<img src={trash} alt="botão excluir" className="imgsbtns" />
</button>
<button className="click-img">
<img src={edit} alt="botão editar" className="imgsbtns" />
</button>
</div>
)
}
}
}
the modal:
return (
<Modal
{...props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
style={{ fontFamily: "Questrial" }}
>
<Modal.Header>
<Modal.Title id="contained-modal-title-vcenter">
DELETE THE POST
</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>
are you sure?
</p>
<Button onClick={Delete}>YES</Button>
</Modal.Body>
<Modal.Footer>
<Button onClick={props.onHide}>CLOSE</Button>
</Modal.Footer>
</Modal>
);
}
Add two state:
openDeleteModal to manage opening modal (argument for example, you have this argument yet)
deleteId to store id.
const [openDeleteModal, setOpenDeleteModal] = useState(false);
const [deleteId, setDeleteId] = useState(null);
When you click on post you must open modal and update deleteId, so rewrite onClick function:
onClick={()=>{
setDeleteId(idvalue[i]);
setOpenDeleteModal(true)}
}
Pass deleteId and setOpenModal via props to Modal component and rewrite modal buttons function:
<Modal.Body>
<p>
are you sure?
</p>
<Button
onClick={()=>{
Delete(deleteId)
setOpenDeleteModal(false) //close modal after delete
}}>
YES
</Button>
</Modal.Body>
<Modal.Footer>
<Button onClick={()=>setOpenDeleteModal(false)}>CLOSE</Button>
</Modal.Footer>

Using Reactstrap: How to toggle only one Collapse at a time?

I am using Reactstrap to open and collapse multiple cards. Once opened, they stay open, and since I plan to use more of them (for articles), this will be a mess. Once I click on a button and open a card, I would like the others to close, so only one card is displayed at a time. How can I achieve this?
const [isOpenInfo, setIsOpenInfo] = useState(false);
const toggleInfo = () => setIsOpenInfo(!isOpenInfo);
const [isOpenArticle1, setIsOpenArticle1] = useState(false);
const toggleArticle1 = () => setIsOpenArticle1(!isOpenArticle1);
const [isOpenArticle2, setIsOpenArticle2] = useState(false);
const toggleArticle2 = () => setIsOpenArticle2(!isOpenArticle2);
In my menu, I have a button "More Info", when clicked, it opens a list of collapsed articles and when clicking on each title, it opens the article (but I just want one article to open at a time). So it's like a collapse inside a collapse...
<Button className="info-button" color="primary" onClick={toggleInfo}>
More Info
</Button>
<Collapse isOpen={isOpenInfo}>
<Card className="card">
<CardBody className="card-body">
<div className="section section-articles">
<div className="articles-buttons">
<Button
className="article2-button"
color="primary"
onClick={toggleArticle2}
>
<h3>Article 2</h3>
</Button>
<Button
className="article1-button"
color="primary"
onClick={toggleArticle1}
>
<h3>Article 1</h3>
</Button>
</div>
<Collapse isOpen={isOpenArticle2}>
<Card className="card">
<CardBody className="card-body">
<Article2 />
</CardBody>
</Card>
</Collapse>
<Collapse isOpen={isOpenArticle1}>
<Card className="card">
<CardBody className="card-body">
<Article1 />
</CardBody>
</Card>
</Collapse>
</div>
</CardBody>
</Card>
</Collapse>
You can create a default variable consisting all your articles and use it to set the state. Create a single state variable for all of your collapsible.
const DEFAULT_ARTICLES = {
article1: false,
article2: false,
};
const [articles, setArticles] = useState(DEFAULT_ARTICLES);
const toggleArticle = (key) => setArticles({
...DEFAULT_ARTICLES,
[key]: true,
});
And on your render function use the key to open the collapse and toggle the collapse.
<Collapse isOpen={isOpenInfo}>
<Card className="card">
<CardBody className="card-body">
<div className="section section-articles">
<div class="articles-buttons">
<Button
className="article2-button"
color="primary"
onClick={() => toggleArticle('article2')}
>
<h3>Article 2</h3>
</Button>
<Button
className="article1-button"
color="primary"
onClick={() => toggleArticle('article1')}
>
<h3>Article 1</h3>
</Button>
</div>
<Collapse isOpen={articles['article1']}>
<Card className="card">
<CardBody className="card-body">
<Article2 />
</CardBody>
</Card>
</Collapse>
<Collapse isOpen={articles['article2']}>
<Card className="card">
<CardBody className="card-body">
<Article1 />
</CardBody>
</Card>
</Collapse>
</div>
</CardBody>
</Card>
</Collapse>
You can use one state to control all the collapses.
const [openedCollapse, setOpenedCollapse] = useState("");
const openCollapse = e => { // this is the button onClick handler
setOpenedCollapse(e.target.dataset.collapse);
};
Then your jsx looks like this:
<Button
className="article1-button"
color="primary"
data-collapse="article1" // A dataset param
onClick={openCollapse}>
<h3>Article 1</h3>
</Button>
<Collapse isOpen={openedCollapse === "article1"}>
<Card className="card">
<CardBody className="card-body">
<Article2 />
</CardBody>
</Card>
</Collapse>
dataset info
You can use an object as state with a callback function when toggling the More Info collapse and then utilize a simple string to determine which article should be opened when the main Collapse is open and a Button was clicked inside of it.
For example, updating whether or not the main collapse is open:
const toggleMoreInfo = () => {
setState(prevState => {
// this gives us access to the current state when setState is executed
// then we can inverse a boolean when the More Info button is clicked
return {
article: "", // resets the open article
moreInfoOpen: !prevState.moreInfoOpen // false => true || true => false
}
})
}
For example, updating which article should be opened:
const handleArticleOpen = (article) => {
setState((prevState) =>
return {
// keep whatever is in state as is by spreading it out (in this case, "moreInfoOpen" stays unchanged)
...prevState, //
// and just override the article with a passed in string
article
}));
};
When dealing with coupled state, I like to use objects over individual states, since it's easier to keep both sets of state in sync. For a demo and the full code, look below...
Working demo:
Code
import * as React from "react";
import { Button, Card, CardBody, Collapse } from "reactstrap";
import "./styles.css";
import "bootstrap/dist/css/bootstrap.min.css";
export default function App() {
const [state, setState] = React.useState({
articleOpen: "",
moreInfoOpen: false
});
const { article, moreInfoOpen } = state;
const toggleMoreInfo = () => {
setState((prevState) => ({
article: "",
moreInfoOpen: !prevState.moreInfoOpen
}));
};
const handleArticleOpen = (article) => {
setState((prevState) => ({
...prevState,
article
}));
};
return (
<div className="app">
<Button className="info-button" color="primary" onClick={toggleMoreInfo}>
More Info
</Button>
<Collapse isOpen={moreInfoOpen}>
<Card className="card">
<CardBody className="card-body">
<div className="section section-articles">
<div className="articles-buttons">
<Button
className="article2-button"
color="primary"
onClick={() => handleArticleOpen("2")}
>
<h3>Article 2</h3>
</Button>
<Button
className="article1-button"
color="primary"
onClick={() => handleArticleOpen("1")}
>
<h3>Article 1</h3>
</Button>
</div>
<Collapse isOpen={article === "2"}>
<Card className="card">
<CardBody className="card-body">
<div>Article 2</div>
</CardBody>
</Card>
</Collapse>
<Collapse isOpen={article === "1"}>
<Card className="card">
<CardBody className="card-body">
<div>Article 1</div>
</CardBody>
</Card>
</Collapse>
</div>
</CardBody>
</Card>
</Collapse>
</div>
);
}

How can I toggle between states of a button in React?

I'm using Material UI for the icons for connect and disconnect.
I want the application to swap between these two icons onClick.
I'm new to React, but haven't any helpful resources yet.
Here's the code as it is so far:
{ user.connected ?
(
<Button color="info" simple size="sm">
<PersonAddDisabled className={classes.footerIcons} /> Disconnect
</Button>
)
:
(
<Button color="info" size="sm">
<PersonAdd className={classes.footerIcons} /> Connect
</Button>
)
}
As it is above, I am address whether they are connected or not, but I'm not sure how to implement a toggle of those two buttons, where onClick it will switch between the two.
here is a code
Your state
state = {
connected: true
}
onClickButton(){
this.setState(prevState => {connected: !this.prevState.connected})
}
Your code
{ this.state.connected ?
(
<Button color="info" simple size="sm" onClick={ this.onClickButton }>
<PersonAddDisabled className={ classes.footerIcons } /> Disconnect
</Button>
)
:
(
<Button color="info" size="sm" onClick={ this.onClickButton }>
<PersonAdd className={ classes.footerIcons } /> Connect
</Button>
)
}

Reactstrap modals all open on button click

I'm having trouble with the reactstrap modals. I'm looping through an array and it makes modals and buttons, and gets information in a nice card.
Now when I click on a button to open the modal, all modals open even if I didn't click the button on the card. All (39) open. I was hoping you all could help me out <3
Modal State
this.state= {
modal:false,
}
Toggle Function
toggle(){
this.setState(prevState =>({
modal: !prevState.modal
}));
}
Everything that gets returned
return (
<div key={item.ID}>
<ul>
<Modal isOpen={this.state.modal} toggle={this.modal} id={item.ID}>
<ModalHeader id={item.ID} toggle={this.toggle}>{item.Naam}</ModalHeader>
<ModalBody>fdass</ModalBody>
<ModalFooter><Button onClick={this.toggle} color='primary'>fddss</Button></ModalFooter>
</Modal>
<Card style={{ width: '18rem' }}>
{/* <CardImg src={"http://static.floraxchange.nl/artikelen/"+ item.Fotos.ID +"_v_t8.jpg"} alt={item.Naam} /> */}
<Card.Body>
<Card.Title>{item.Naam}</Card.Title>
<Card.Text>
Hoogte: {item.Hoogte}<br />
Potmaat: {item.Potmaat} <br/>
ID: {item.ID}
</Card.Text>
<Button color="danger" data-target={'#' + item.ID} onClick={this.toggle}>More information</Button>
</Card.Body>
</Card>
</ul>
</div>
)

Categories

Resources