Unable to trigger Modal from button within table row in React - javascript

I have two files: Modal.js and Users.js. Users.js contains a table that has an API get query mapped to it, in the final column of the table is a a dropdown for each row that contains three buttons; View, Edit and Delete. I would like the Delete button to trigger the Modal containing a message that roughly reads "Are you sure you wish to delete the user?".
I am struggling to get the Modal to trigger in the onClick event of the Delete component in the Users.js file, I'll attach the code for the two files below.
Modal.js (I have not edited any of the content in the modal yet)
import {
CButton,
CModal,
CModalHeader,
CModalTitle,
CModalBody,
CModalFooter,
} from "#coreui/react";
const Modal = ({ visible, setVisible }) => {
return (
<>
<CModal visible={visible} onClose={() => setVisible(false)}>
<CModalHeader onClose={() => setVisible(false)}>
<CModalTitle>Modal title</CModalTitle>
</CModalHeader>
<CModalBody>Woohoo, you're reading this text in a modal!</CModalBody>
<CModalFooter>
<CButton color="secondary" onClick={() => setVisible(false)}>
Close
</CButton>
<CButton color="primary">Save changes</CButton>
</CModalFooter>
</CModal>
</>
);
};
export default Modal;
Users.js
<CTableRow v-for="item in tableItems" key={rows.userID}>
<CTableDataCell className="text-center">{rows.userID}</CTableDataCell>
<CTableDataCell>{rows.firstName}</CTableDataCell>
<CTableDataCell>
<div>{rows.lastName}</div>
</CTableDataCell>
<CTableDataCell className="column-overflow">{rows.email}</CTableDataCell>
<CTableDataCell>{rows.role}</CTableDataCell>
<CTableDataCell>{rows.createdAt}</CTableDataCell>
<CTableDataCell>{rows.updatedAt}</CTableDataCell>
<CTableDataCell>
<strong>{rows.lastLogin}</strong>
</CTableDataCell>
<CTableDataCell>
<CDropdown>
<CDropdownToggle color="transparent"></CDropdownToggle>
<CDropdownMenu>
<CDropdownItem className="dropdown-item pointer">View</CDropdownItem>
<CDropdownItem className="dropdown-item pointer">Edit</CDropdownItem>
<CDropdownItem
className="dropdown-item text-danger pointer"
onClick={() => Modal()} <- Issue here
>
Delete
</CDropdownItem>
</CDropdownMenu>
</CDropdown>
</CTableDataCell>
</CTableRow>;
I would appreciate any assistance with this. Let me know if I can give any other code (I have reduced the Users.js file as it is just over 160 lines long and I don't want to clutter, except for the row so you can get an idea of where the Delete button lives).
Thanks in advance!

The Modal component needs to be in the page with a visible prop controlled by the parent (and not called as a function). The CoreUI examples for CModal are a bit misleading for your use-case. I suggest using useState in your parent Users component and passing a setter callback to the Modal for the close event.
For example:
Users.js
const Users = () => {
const [visible, setVisible] = React.useState(false);
const [selectedUser, setSelectedUser] = React.useState(null);
const rows = [
{ userID: "1", firstName: "Cameron", lastName: "E" },
{ userID: "2", firstName: "Steve", lastName: "G" }
];
return (
<>
<CTable>
{rows.map((row) => {
return (
<CTableRow v-for="item in tableItems" key={row.userID}>
<CTableDataCell className="text-center">
{row.userID}
</CTableDataCell>
<CTableDataCell>{row.firstName}</CTableDataCell>
<CTableDataCell>
<div>{row.lastName}</div>
</CTableDataCell>
<CTableDataCell className="column-overflow">
{row.email}
</CTableDataCell>
<CTableDataCell>{row.role}</CTableDataCell>
<CTableDataCell>{row.createdAt}</CTableDataCell>
<CTableDataCell>{row.updatedAt}</CTableDataCell>
<CTableDataCell>
<strong>{row.lastLogin}</strong>
</CTableDataCell>
<CTableDataCell>
<CDropdown>
<CDropdownToggle color="transparent">
Dropdown button
</CDropdownToggle>
<CDropdownMenu>
<CDropdownItem className="dropdown-item pointer">
View
</CDropdownItem>
<CDropdownItem className="dropdown-item pointer">
Edit
</CDropdownItem>
<CDropdownItem
className="dropdown-item text-danger pointer"
onClick={() => {
setSelectedUser(row);
setVisible(true);
}}
>
Delete
</CDropdownItem>
</CDropdownMenu>
</CDropdown>
</CTableDataCell>
</CTableRow>
);
})}
</CTable>
<Modal
visible={visible}
user={selectedUser}
onClose={() => setVisible(false)}
/>
</>
);
};
Modal.js
const Modal = ({ visible, onClose, user }) => {
return (
<>
<CModal visible={visible} onClose={onClose}>
<CModalHeader onClose={onClose}>
<CModalTitle>Delete {user.firstName}?</CModalTitle>
</CModalHeader>
<CModalBody>
Are you sure you want to delete {user.firstName}? (He's a good guy.)
</CModalBody>
<CModalFooter>
<CButton color="secondary" onClick={onClose}>
Yeah he is a good guy
</CButton>
<CButton color="primary">Nuke 'Em!</CButton>
</CModalFooter>
</CModal>
</>
);
};
Note: As an added bonus, I added an example of passing the selected user to the Modal so only one instance of the Modal component exists in the page.
Working CodeSandbox: https://codesandbox.io/s/cold-leftpad-l5ivqy?file=/src/Modal.js
Users.js Rendered
Modal.js Rendered

Related

Send props to another functional component on click

I just learned react and I am building a simple blog app in react and I am trying to send props on button click But the props is not showing in the another functional component.
I am using react-bootstrap for modal (which is second component). And I am using for edit the current blog when user click on edit button in first component.
App.js
function SecondModalComponent(props) {
return (
<>
<Modal show={props.show}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>
<input type="text" value={props.title} />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={props.onHide}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
)
}
const response = [
{
title: "First Blog",
description: "First Blog"
},
{
title: "Second Blog",
description: "First Blog"
},
{
title: "Third Blog",
description: "First Blog"
}
]
function BlogFirstComponent() {
const [show, setShow] = useState(false);
const openEditModal = (title) => {
<SecondModalComponent
title={title}
/>
}
return (
<>
<SecondModalComponent
show={modalShow}
onHide={() => setShow(false)}
/>
{
response.map((data) =>
<div>
<b>Title</b> {data.title}
<b>Title</b> {data.description}
<span onClick={() => openEditModal(data.title)}>Edit</span>
</div>
)}
</>
)
}
I have tried many times but it is still no showing the prop title.
Any help would be much Appreciated.
this is not how react model works with UI component, component should be declarative, and not returned on a callback on click handler.
you can define your modal at the top level of your component
function BlogFirstComponent() {
const [secondModalOpen, setSecondModalOpen] = useState(false);
const [data, setData] = useState({ title: '', description: '' });
return (
<>
<SecondModalComponent
{...data}
show={secondModalOpen}
onHide={() => setSecondModalOpen(false)}
/>
...
{
response.map((data) =>
<div>
<b>Title</b> {data.title}
<b>Title</b> {data.description}
<span onClick={() => {
setData({...data});
setSecondModalOpen(true);
}>Edit</span>
</div>
)}
<>
);
}
Now, whenever someone clicks the button, the modal is opened, and the right props are passed to the component at that moment.

How to make rowEvents in react-bootstrap-table-next be called on a button only instead of a row

I am working on a react-bootstrap-table, I set it up in a way when I click on the icon button, a modal pops up displaying the information of the row data. But when I click on another column of a row, the modal also pops up displaying data.
I want a situation whereby the modal only pops up when you click on the icon, instead of the row. This is making the cellEdit function not to be called.
const [modalInfo, setModalInfo] = useState([])
const [show, setShow] = useState(false);
const [showModal, setShowModal] = useState(null);
const rowEvents = {onClick: (e, row) => {
setModalInfo(row);
toggleTrueFalse();
},
};
const toggleTrueFalse = () => setShowModal(handleShow);
Cell Edit
const cellEdit = cellEditFactory({
mode: 'click',
blurToSave: true,})
Modal
const ModalContent = () => {
return (
<>
<Modal isOpen show={show}>
<ModalHeader>Terminal Info</ModalHeader>
<ModalBody>
<ul>
<h6 style={{ fontFamily: 'Georgia' }}>id : {modalInfo.id}</h6>
</ul>
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={handleClose}>
<FontAwesomeIcon icon="ban" />
Close
</Button>
</ModalFooter>
</Modal>
</>
);
};
jsx
return (
<>
<div>
<h2 style={{ color: "red", fontFamily: "Georgia" }}>Terminals</h2>
</div>
<BootstrapTable
keyField="id"
data={data}
columns={columns}
cellEdit={cellEdit}
rowEvents={rowEvents}
/>
{show ? <ModalContent /> : null}
</>
);
If I can be able to achieve this, then cellEdit will work fine.

Passing a single boolean useState between two pages using Material UI web components

I've been trying to pass in a boolean useState between two pages, using the best practice shown here: How to call useState from another Page?
My main objective is to show a success alert on a different page, once a form is successfully submitted on "./demo"
Demo.js holds a dialog with a submit button that sets the setSuccess to true.
import Alert from "./alert";
export default function AlertDialog() {
const [success, setSuccess] = React.useState(false); // <- Hides and Shows the Alert Message
const handleSubmit = () => {
return (
<Alert
setSuccess={() => {
setSuccess(true); // <- How I am trying to setSuccess to true.
}}
/>
);
};
return (
<DialogActions>
<Button
onClick={handleSubmit}
color="primary"
autoFocus
component={RouterLink}
to={"/"}
>
Submit
</Button>
</DialogActions>
);
Alert.js has an alert message that appears once success is set to true.
export default function Alerts(props) {
// const [open, setOpen] = React.useState(false);
const { success, setSuccess } = props;
return (
<div>
<Collapse in={success}>
<Alert
action={
<IconButton
aria-label="close"
color="inherit"
size="small"
onClick={() => {
setSuccess(false);
}}
>
<CloseIcon fontSize="inherit" />
</IconButton>
}
>
Form Successfully Submitted
</Alert>
</Collapse>
<Button
disabled={success}
variant="outlined"
component={RouterLink}
to={"/demo"}
>
Go Back to Submit Form
</Button>
</div>
)
;
}
Could someone explain how I can have the success alert appear after submission? If you wish to take a deeper dive, please visit here https://codesandbox.io/s/alert-test-qhkbg?file=/alert.js
I think what you're looking for here is passing state through React Router. Right now your alert isn't updating because your URL is changing when the submit button is pressed.
Check out this sandbox. I'm passing a message and a property to get the alert to render within the / route with different logic.
This is the key snippet:
<Button
color="primary"
autoFocus
component={RouterLink}
to={{
pathname: "/",
state: { message: "hello, i am a state message", open: true }
}}
>
Submit
</Button>
And then in your alert component at the url / you can:
const [open, setOpen] = React.useState(false);
React.useEffect(() => {
props.location.state?.open ? setOpen(true) : setOpen(false);
}, [props]);
// This message is what is being passed. Could be anything.
console.log(props.location.state);

Dispatch a Redux action from a modal in React

I'm a begginer in React and I'm trying to find a way to dispatch a redux action from a modal.
I have a list of products and a button 'add to bag' under each image. When we click on the button 'add to bag', I want a modal to appear that ask for a confirmation. The action need to be dispatched when the user click on the confirm button inside de modal window.
The action need to grab the item object.
All is working fine ...but I'm not able to pass the item into the action when I want to launch the action from the modal.
So, my problem is not the Redux side but the modal part ( I use React-bootstrap for the modal).
I have this error message : 'item' is not defined
I'm not sure I understand exactly why this does'nt work and I failed to find a solution. I tried so many things but it's just not working.
Is there a simple way to add/pass the item data into the modal easily ?
Help would be very appreciated ! :)
Thanks a lot !!!
Here are part of my files :
Product.js
import { Modal } from "react-bootstrap";
function Products(props) {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div>
<CardGroup>
{_.map(props.data, (item) => (
<Col>
...Some code here
<div>
<div>
{item.name.map((item) => (
<ul key={item}>{item}</ul>
))}
</div>
<div>
<button onClick={() => {handleShow()}}>
Add to bag
</button>
</div>
</div>
</Col>
))}
</CardGroup>
<Modal show={show} onHide={handleClose}>
<Modal.Body>Please confirm you want to add this product</Modal.Body>
<Modal.Footer>
<button
onClick={props.addProductToBasket(item)}
>
Confirm
</button>
<button onClick={handleClose}>Cancel</button>
</Modal.Footer>
</Modal>
</div>
);
}
const mapStateToProps = (state) => {
return { code here ...};
};
const mapDispatchToProps = (dispatch) => {
return {
addProductToBasket: (id) => dispatch(addProductToBasket(id)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Products);
Here is the part of my Product.js file im my Store/actions folder
export const addProductToBasket = (id) => {
return {
type: ADD_PRODUCTD_TO_BASKET,
payload: id,
};
};
Since you only show the modal if an item was clicked, store the item or null instead of Boolean in the state, and open and close the modal accordingly:
function Products(props) {
const [selectedItem, setShowItem] = useState(null); // item for open model or null for closed
const handleClose = () => setShowItem(null);
const handleShow = item => setShowItem(item);
return (
<div>
<CardGroup>
{_.map(props.data, (item) => (
<Col>
...Some code here
<div>
<div>
{item.name.map((item) => (
<ul key={item}>{item}</ul>
))}
</div>
<div>
<button onClick={() => handleShow(item)}>
Add to bag
</button>
</div>
</div>
</Col>
))}
</CardGroup>
<Modal show={selectedItem !== null} onHide={handleClose}>
<Modal.Body>Please confirm you want to add this product</Modal.Body>
<Modal.Footer>
<button
onClick={() => props.addProductToBasket(selectedItem)}
>
Confirm
</button>
<button onClick={handleClose}>Cancel</button>
</Modal.Footer>
</Modal>
</div>
);
}
Not related, but it will make your life easier - use the object form of mapDispatchToProps that will save you the need to wrap with dispatch manually:
const mapDispatchToProps = {
addProductToBasket
};

Displaying bootstrap modal in reactJs

I am trying to display a bootstrap modal in my reactJs project. I have buttons with different IDs with different content displayed in a map function. I want the each modal content to correspond to the ID of the button clicked. I am following this examaple - https://react-bootstrap.github.io/components/modal/ but there is no way to specify the id here.
How do I resolve this?
At button, you can pass content to onClick and set it to state
handleShow = (data) => {
this.setState({ show: true, title: data.title });
}
render() {
return (
<div>
{
listData.map((data, index) => {
return (
<Button key={index} bsStyle="primary" bsSize="large" onClick={() => this.handleShow(data)}>
Launch demo modal
</Button>
);
})
}
</div>
<Modal show={this.state.show} onHide={this.handleClose}>
<Modal.Body>
{this.state.title}
</Modal.Body>
</Modal>

Categories

Resources