Mapping to React Modal only renders the last item of the array - javascript

My issue is that when I map the data into the Modal, it is only returning the last item in the array. It looks like it is actually opening a modal for every object in the array and stays open on the last object.
I am also using React-Bootstrap-Table-Next in the same component. The button that I want to trigger the Modal with is currently in a column on the table. I would appreciate some input as I feel like I have hit a wall.
Button being rendered in the table with onClick handler to open the modal:
const columns = [
{ dataField: "View", text: "View",
formatter: () => {return <Button variant="success" onClick={handleShow}>View</Button>},
headerStyle: () => {return { width: "5%" };} },
{ dataField: "BusinessName", text: "Business Name", headerStyle: () => {return { width: "27%" };} },
];
Modal state handling:
const [showModal, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
Modal code with map function:
<div>
{submitted.map((submitted) => (
<Modal
show={showModal}
onHide={handleClose}
size="lg"
key={submitted.Id}
centered
>
<Modal.Header closeButton>
<Modal.Title>{submitted.BusinessName}</Modal.Title>
</Modal.Header>
<Modal.Body>{submitted.Email}</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
))}
</div>
Getting and setting the data:
const [submitted, setSubmitted] = useState([]);
const getSubmittedData = async () => {
try {
const response = await Axios.get(url);
setSubmitted(response.data);
console.log(response.data);
setLoading(true);
} catch (e) {
console.log(e)
}
};

I'm not entirely sure what you are trying to do. If you click in a button to show a modal with the information of just one business, you wouldn't need to use map. You just pass the object you need into the Modal and use that:
...
<Modal.Body>
<div>Business Name: {business.BusinessName}</div>
<div>Business Email: {business.Email}</div>
</Modal.Body>
...
business would be one record filtered from all records (eg: by email), something like:
const business = submitted.find(({email}) => email === 'some.email#example.com')
But, if you want to have a list of submitted businesses showing in a modal, you need to only include the repeatable elements in the map.
In this case we have a modal with its different parts (Header, Footer, Body, etc) which should be an individual object being rendered. You then need to map the contents of your submitted list and output what you need.
Something like:
<div>
<Modal show={showModal} onHide={handleClose} size="lg" centered>
<Modal.Header closeButton>
<Modal.Title>Businesses</Modal.Title>
</Modal.Header>
<Modal.Body>
{submitted.map((submitted) => (
<div key={submitted.Id}>
<div>Business Name: {submitted.BusinessName}</div>
<div>Business Email: {submitted.Email}</div>
</div>
))}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>

Related

How to use react-bootstrap modal in map function

I created table with some date but too better user experience I want to display it on modal. My problem is that on modal I see only first of list name and photo.
I try to do modal in component too code look more readable.
const [show, setShow] = useState(false);
const open = () => {
setShow(true)
}
return (
(..)
<thead><tr><th>miniaturka</th><th>nazwa</th><th>cena</th></tr></thead>
<tbody >
{zestawy.map((zestawy, zestaw) => {
return(<>
<tr key={zestawy.id}>
<th><Image src={`/nowemodule/${zestaw}.webp`}
width={100} height={100}/> </th>
<td>
<Button onClick={open}>
{zestawy.name} {zestawy.wielkości}</Button>
</td>
<td>{cena(zestaw).toFixed(2) }zł</td>
</tr>
<ModalPhoto
show={show} setShow={setShow}
title={zestawy.name}
content={zestawy.description}
photo={`/nowemodule/${zestaw}.webp`}
/>
</>
)
})}
</tbody>
</Table>
and mosal.jsx
function ModalPhoto({title, content, photo, show, setShow}) {
const handleClose = () => setShow(false);
return (
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>{title}</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{content}</p>
</Modal.Body>
<Image src={photo} width={100} height={100}/>
<Modal.Footer>
<Button variant="secondary">Close</Button>
</Modal.Footer>
</Modal>
);
}
export default ModalPhoto;
The big problem is that you use a unique show state for all indexes of zestawy array. So when you set show = true, all iterations of ModalPhoto open. You only see the last one in HTML.
To fix this, you can set a showByZestawy object that contains zestawy id as key and a boolean for open or not in value :
const [showByZestawy, setShowByZestawy] = useState({});
const handleOpen = (zestawy) => {
setShowByZestawy({...showByZestawy, [zestawy.id]: true })
}
const handleClose = (zestawy) => {
const nextShowByZestawy = {...showByZestawy};
delete nextShowByZestawy[zestawy.id];
setShowByZestawy(nextShowByZestawy)
}
//...
<ModalPhoto show={showByZestawy[zestawy.id]} onHide={handleClose} />
You set key={zestawy.id} on <tr> in your map but this is an error because the first child of your map is a Fragment that contains the Modal.
So you should do :
<Fragment key={zestawy.id}> // nameless Fragments like <> cannot have a key props
<tr>{...}</tr>
<ModalPhoto/>
</Fragment>

Axios Delete operation on React-bootstrap-table displaying [object%20Object] on the browser's console

I am trying to perform an axios delete operation on a react-bootstrap-table, but I am getting this error on the console
DELETE http://localhost:9000/api/terminals/[object%20Object]
Uncaught (in promise) Error: Request failed with status code 400
These are the codes
const
const apiUrl = 'api/terminals';
UseState and UseEffect
const [data, setData] = useState([]);
useEffect(() => {
getData();
}, []);
const getData = () => {
axios(apiUrl).then(res => {
setData(res.data);
});
};
Axios delete
const handleDelete = rowId => {
axios.delete(`${apiUrl}/${rowId}`).then(() => setData(data));
};
Modal Content
const ModalContent = () => {
return (
<>
<Modal show={showModal} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Confirm delete operation</Modal.Title>
</Modal.Header>
<Modal.Body>Are you sure you want to delete this Terminal?
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Cancel
</Button>
<Button onClick={id => handleDelete(id)}>Confirm</Button>
</Modal.Footer>
</Modal>
</>
);
};
main jsx with bootstrap Table
<>
<BootstrapTable
keyField="id"
data={data}
columns={columns}
striped
hover
condensed
pagination={paginationFactory()}
cellEdit={cellEdit}
filter={filterFactory()}
noDataIndication="Table is Empty"
/>
{showModal ? <ModalContent /> : null}
</>
You use an object instead of a string in the delete url.
Normally you get an event from a onClick and not the id.
If you get the id from somewhere else in the code that you do not show the you could try this.
<Button variant="primary" onClick={() => handleDelete(id)}>
Confirm
</Button>

how to show react bootstrap modal inside the function

I am using react bootstrap modal popup. when user submit form, I need to modal show popup,
This is my modal (SaveConfirmationModal )
import { Button, Modal} from "react-bootstrap";
function SaveConfirmationModal(props) {
return (
<div>
<Modal {...props} size="lg">
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
do u want to save ?
</Modal.Title>
</Modal.Header>
<Modal.Body>
<button>YEs </button>
<button>No</button>
</Modal.Body>
</Modal>
</div>
);
}
export default SaveConfirmationModal;
this is my invoice page save function. I imported my modal in to the invoice page.
import SaveConfirmationModal from "components/createinvoice/SaveConfirmationModal";
const loadPopup= (data) => {
showmodal;
if(yes){
saveForm();
}
else{
close modal
}
}
const saveForm= (data){
my save function
}
my save button in the invoice page
<button onClick={loadPopup}> Save </button>
This is the very little sample to demonstrate my issue. If you can please help me to show this confirmation box. thanks
In the component where you use your SaveConfirmationModal, you can use state Hooks, like this:
import React, {useState} from 'react';
...
const [showModal, setShowModal] = useState(false);
const loadPopup = () => {
setShowModal(true);
};
And you need to change the state to false when the modal is dismissed:
<SaveConfirmationModal show={showModal} onHide={() => setShowModal(false)}
You can call a function from a prop on button click like below.
import { Button, Modal} from "react-bootstrap";
function SaveConfirmationModal(props) {
return (
<div>
<Modal {...props} size="lg">
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
do u want to save ?
</Modal.Title>
</Modal.Header>
<Modal.Body>
<button onClick={props.onYes}>YEs </button>
<button onClick={props.onNo}>No</button>
</Modal.Body>
</Modal>
</div>
);
}
export default SaveConfirmationModal;
Then in your invoice page create a state for showing a modal and pass the props like below.
import { useState } from 'react'
import SaveConfirmationModal from "components/createinvoice/SaveConfirmationModal";
const InvoicePage = () => {
const [showModal, setShowModal] = useState(false);
const saveForm = (data) {
// my save function
}
return (
<>
<button onClick={() => setShowModal(true)}> Save </button>
<SaveConfirmationModal show={showModal} onYes={() => saveForm(data)} onNow={() => setShowModal(false)} />
</>
)
}

TypeScript React Invalid Hook Call Error ("Error: Invalid hook call.")

I tried asking a question earlier but realized it was a bit all over the place so no one could really understand it properly. So here's a more clarified attempt.
I am currently working on a CRUD web project that allows the user to edit, view and create more users which is then displayed on a table.
I have been using react-bootstrap's components so decided to use the Modal component provided. (https://react-bootstrap.netlify.com/components/modal/#modals-live)
It was successfully working without any errors when I just called and used the component within App.tsx as follows:
const App: React.FC = () => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div className="App">
<NavBar/>
<Jumbotron fluid className="userJumbotron">
<Container>
<h1>Manage Users</h1>
<Button variant="outline-dark" onClick={handleShow}>Add new user</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Add User</Modal.Title>
</Modal.Header>
<Modal.Body><NewUserForm/></Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
But I think realized that I'll need more than 1 modal component for my program and decided to make the modal component section into my own component that I can reuse as many times as I want and customize as I want within the App.tsx
So I decided to make a NewModal.tsx component that has the bootstrap modal component and button pre-made meaning I'd only have to call one line () rather than a whole chunk. the NewModal.tsx code is as follows:
export default class NewModal extends Component<any> {
constructor (props: any){
super(props)
this.state={
show:false
}
}
render() {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div>
<h1>Manage Users</h1>
<Button variant="outline-dark" onClick={handleShow}>Add new user</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Add User</Modal.Title>
</Modal.Header>
<Modal.Body><NewUserForm/></Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}
I am getting the following error from this code.
What is the cause for this error?
You can't use hooks inside class components, you need to change it to be a function based component, which can look something like this:
const NewModal = () => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div>
<h1>Manage Users</h1>
<Button variant="outline-dark" onClick={handleShow}>
Add new user
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Add User</Modal.Title>
</Modal.Header>
<Modal.Body>
<NewUserForm />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
export default NewModal;

Can successfully open modal when I need to but cannot close it

I am trying to use React boostrap modal, I can successfully open it when i need to but I cannot close it. I have no idea what i am doing wrong.
my markup
``` <Modal show={this.props.show} >
<Modal.Header>
<Modal.Title>Enter Log</Modal.Title>
</Modal.Header>
<Modal.Body>
Hello i am Modal!!!!!
</Modal.Body>
<Modal.Footer>
<Button onClick={() => {closeModal()}} variant="primary"> Close </Button>
</Modal.Footer>```
state:
constructor(props) {
super(props)
this.state = {
closeModal: false
}
}
Handler function:
const closeModal = () => {
this.setState({closeModal: true});
}
You can do this way,
const closeModal = () => {
this.setState({ show: false});
}
<Modal show={this.props.show} onHide={this.props.closeModal} >
<Modal.Header closeButton>
<Modal.Title>Enter Log</Modal.Title>
</Modal.Header>
<Modal.Body>
<p> Hello i am Modal!!!!! </p>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.props.closeModal} variant="primary"> Close </Button>
</Modal.Footer>
</Modal>
refrence https://react-bootstrap.github.io/components/modal/
You need to use the same condition to check if render the modal or not
You are using props to check if your modal should be rendered and when closing it, you are setting state to false.
If you want to use props you will need to update your props, using redux and dispatching an action or whatever
According to yor handleClose function, you will need to check state instead of props in the condition
const closeModal = () => {
this.setState({closeModal: true});
}
<Modal show={this.props.show} >
<Modal.Header>
<Modal.Title>Enter Log</Modal.Title>
</Modal.Header>
<Modal.Body>
Hello i am Modal!!!!!
</Modal.Body>
<Modal.Footer>
<Button onClick={() => this.closeModal()} variant="primary"> Close </Button>
</Modal.Footer>

Categories