I am creating a web application with react.js also using react-bootstrap.
What I want to accomplish is when user click on button then modal will appear but that is not happening.
Button code
<div className="rightButtons">
<button name="updateItem" className="btn btn-primary" onClick={() => { this.onUpdateItem(this.props.selectedAppTypeId)}} >Update Item</button>
</div>
function which will be called.
onUpdateItem = (id) => {
return (
<div className="static-modal">
<Modal.Dialog>
<Modal.Header>
<Modal.Title>Modal title</Modal.Title>
</Modal.Header>
<Modal.Body>One fine body...</Modal.Body>
<Modal.Footer>
<Button>Close</Button>
<Button bsStyle="primary">Save changes</Button>
</Modal.Footer>
</Modal.Dialog>
</div>
);
}
Any help would be great help. Thanks.
You are returning HTML into your onClick handler, and not into the Component's render method. If you want to show HTML content after an onClick, you need to actually return those HTML elements within the actual render. Try this:
class Component extends React.Component {
state = {
showModal: false
}
renderModal = () => {
return (
<div className="static-modal">
<Modal.Dialog>
<Modal.Header>
<Modal.Title>Modal title</Modal.Title>
</Modal.Header>
<Modal.Body>One fine body...</Modal.Body>
<Modal.Footer>
<Button>Close</Button>
<Button bsStyle="primary">Save changes</Button>
</Modal.Footer>
</Modal.Dialog>
</div>
);
}
render() {
return(
<div>
<button onClick={() => this.setState({showModal:true})}> Show </button>
{this.state.showModal && this.renderModal()}
</div>
)
}
}
Edit: You can also export that entire Modal return statement into its own component, and conditionally render the component when the showModal state is true.
const ModalComponent = (props) => {
return(
<div className="static-modal">
<Modal.Dialog>
<Modal.Header>
<Modal.Title>Modal title</Modal.Title>
</Modal.Header>
<Modal.Body>One fine body...</Modal.Body>
<Modal.Footer>
<Button>Close</Button>
<Button bsStyle="primary">Save changes</Button>
</Modal.Footer>
</Modal.Dialog>
</div>
)
}
class ParentComponent extends React.Component {
state = {
showModal: false
}
render(){
return(
<div>
<button onClick={() => this.setState({showModal:true})}>
Show Modal
</button>
{this.state.showModal && <ModalComponent id='0af141random'/>}
</div>
)
}
}
The problem that you have is that you're passing a react function component as an event handler; the return value of the function is a react component, but calling the function by itself won't mount that component in the DOM and display it.
A better way to make this happen might be to place your Modal component in your component hiearchy, and control whether it is shown by using a callback to change the state of a parent component.
Something like this:
class ParentComponent extends React.Component {
showModal() {
this.setState{
modalActive: true;
}
}
render() {
<div>
<Button onClick={showModal}>Show me the modal!</Button>
<Modal active={this.state.modalActive} />
</div>
}
}
const modalComponent = (props) => {
return (<div style={{ display: (props.isActive) ? "block" : "none "}}>
<h1>I'm a modal!</h1>
</div>)
}
I haven't checked, but I'm sure React Bootstrap has its own prop for displaying/hiding modals.
EDIT: Here's a code example from the react bootstrap documentation that shows it in use https://react-bootstrap.github.io/components/modal/#modals-live
Related
In my App.js, I have a class component Addition which will display 2 numbers and have a text box to get the result from the user. In the same file I have another class component App from which when I try to call <Addition/>, it renders the form perfectly as expected. However, I want the <Addition/> component to be triggered on a button click, which is not working.
class Addition extends Component{
render(){
return (
<form onSubmit={this.validateResult}>
<div >
<Box1 val={this.state.value1}/>
<Box1 val="+"/>
<Box1 val={this.state.value2}/>
<Box1 val="=" />
<input className="resbox"
value={this.state.result}
onChange={event => this.setState({result: event.target.value})}
required
/>
<br/>
<Box2 val={this.state.message} />
<br/><br/>
<input className="btn btn-info" type="submit" value="Submit" />
</div>
</form>
)
}
}
export default class App extends Component {
handleClick= (event) => {
event.preventDefault()
console.log("inside handleclick")
return (
<Addition />
);
}
render(){
return (
<div className="App">
<header className="row App-header">
<p >
KIDS MATHS
</p>
<div className="row col-2">
<button className="btn btn-lg offset-1" onClick={this.handleClick}> + </button>
</div>
</header>
</div>
)
}
}
When I tried to include the <Addition/> within the on-click event, the page simply remains same.
Please advice.
Everything you want to render has to called directly from within the render function. You can use conditional rendering to achieve to selective rendering of Addition. For this add a state member and set the value accordingly at the trigger point. This will call render again and the content will be shown as expected.
export default class App extends Component {
state = {
showAddition: false
}
handleClick= (event) => {
event.preventDefault()
console.log("inside handleclick")
this.setState({showAddition: true})
}
render(){
const { showAddition } = this.state
return (
<div className="App">
<header className="row App-header">
<p >
KIDS MATHS
</p>
<div className="row col-2">
<button className="btn btn-lg offset-1" onClick={this.handleClick}> + </button>
</div>
</header>
{showAddition && <Addition/>}
</div>
)
}
}
You can also consider to convert the components to function components and use useState instead of the state object. This will shorten the code a little bit.
I am passing data to the modal from a parent component. I show the modal on a button click. But the modal body doesn't contain the desired data. It appends all the data inside the body instead of showing only the necessary content.
This is the parent component
{this.state.response.map((value, id) => {
return (
<div className="col-md-3 file-card-viewSection">
<Button
id={id+1}
onClick={(e) => {this.setState({ showDocumentsModal: true })}}>
<i className="fas fa-eye"></i>
</Button>
</div>
<ViewDocumentDetailsModal
show={this.state.showDocumentsModal}
onHide={this.modalClose.bind(this)}
documentdata={value.result} />
);
})}
This is the child component containing the bootstrap modal
class viewDocumentDetailsModal extends Component {
render() {
console.log(this.props)
return (
<Modal
{...this.props}
size="lg"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Modal heading
</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>Centered Modal</h4>
<ReactJson src={this.props.documentdata} />
</Modal.Body>
<Modal.Footer>
<Button onClick={this.props.onHide}>Close</Button>
</Modal.Footer>
</Modal>
);
}
}
When i click on the button it displays both modals at once. I am not able to figure out why. Kindly help me out. Thanks.
This is how I get the output
This is the entire parent component
{this.state.response.map((value, id) => {
return(
<div>
<Card className="mb-4">
<Card.Body className="row file-card-row">
<div className="col-md-1 file-card-index">
{id+1}
</div>
<div className="col-md-4 file-card-filename">
{value.result.Filename}
</div>
<div className="col-md-4 file-card-status">
{value.result.Status}
</div>
<div className="col-md-3 file-card-viewSection">
<Button id={id+1} onClick={(e) => {this.setState({ showDocumentsModal: true, modalOpened: id })}}>
<i className="fas fa-eye"></i>
</Button>
</div>
</Card.Body>
</Card>
</div>
);
})}
<ViewDocumentDetailsModal
show={this.state.showDocumentsModal}
onHide={this.modalClose.bind(this)}
documentdata={this.state.response[this.state.modalOpened].result}
/>
</div>
First, I've tried to adjust the indentation of your code (the edit has to be accepted first), but it seems to me that, in the first piece of code, you return a <div> element and <ViewDocumentDetailsModal> element without any parent component: in React that should not be possible (you should always have a single element returned, if you are in the render() method.
Anyways, I think that you retrieve more than one Modal because <ViewDocumentDetailsModal> element is returned inside the this.state.response.map() callback: this way, for each response you return a different Modal.
If in this.state.response you have data for different Modal, but you want to display just one of them, you need to have a different number property in the state, like this.state.modalOpened, in which you keep saved the index of the modal data you want to display.
Then, in the return() method, you should write something like this:
<>
{this.state.response.map((value, id) => {
return (
<div className="col-md-3 file-card-viewSection">
<Button
id={id+1}
onClick={(e) => {this.setState({ showDocumentsModal: true, modalOpened: id })}}>
<i className="fas fa-eye"></i>
</Button>
</div>
);
})}
<ViewDocumentDetailsModal
show={this.state.showDocumentsModal}
onHide={this.modalClose.bind(this)}
documentdata={this.state.response[this.state.modalOpened].result} />
</>
What I want to do is add bootstrap-react modal and fire it from a stateless function. All the examples I can find requires changing the "show" in the state of component, but since Im not using af component I don't really have an idea how to do it.
https://react-bootstrap.github.io/components/modal/
https://react-bootstrap.github.io/components/modal/
You need state somewhere to show the modal. You can use hooks to do it in functional (not really "stateless") component using useState.
function App() {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="primary" onClick={() => setOpen(true)}>
Launch demo modal
</Button>
<Modal show={open} onHide={() => setOpen(false)}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={() => setOpen(false)}>
Close
</Button>
<Button variant="primary" onClick={() => setOpen(false)}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</>
);
}
codesandbox: https://codesandbox.io/embed/z2ky5l128l
If you don't want to do it then you need to pass prop from component that is higher in tree, like:
class App extends React.Component {
state = {
open: true,
}
render() {
return <ModalComponent open={open} hide={() => this.setState({open: false})} />
}
}
function ModalComponent = ({open, hide}) => (
<Modal show={open} onHide={hide}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={hide}>
Close
</Button>
</Modal.Footer>
</Modal>
)
just pass visible state from parrent like below
class parent extends Component {
state = {
modalVisibility : false
}
handleModalVisibility = (action)=> this.setState({modalVisibility : action})
return (
<div>
<button onClick={this.handleModalVisibility.bind(this,true)} >Show Modal</button>
//use this props to show or cancel the modal
<ModalComponent visibility ={this.state.modalVisibility} handleModal={this.handleModalVisibility} />
</div>
)
}
I guess you can do that with useState
https://reactjs.org/docs/hooks-state.html
New to programming so I'm sorry if I'm not wording this correctly. I'm using a .map to render and list every single item on an array. For each item, I want the modal to open/close only the specific modal corresponding to each item in the array. However, when I click on the button to open the modal, every single one opens and closes. I believe this is because the modals are all set to an on/off button together. How can I set it it (with the .map value.id or something) so that only the specific modal opens and closes?
class DrinkMenu extends Component {
constructor(props, context) {
super(props, context);
this.state = {
show: false
};
this.handleHide = this.handleHide.bind(this);
}
handleHide() {
this.setState({ show: false });
}
async componentDidMount() {
let res = await axios.get('/getAllDrinks')
this.props.updateDrinkMenu(res.data)
}
async addToCart(drinkObj) {
let res = await axios.post('/addToCart', drinkObj)
console.log(`Added ${drinkObj.name} to order.`)
}
render() {
let drinkList = this.props.drinkMenu.map((drink) => {
return (
<div key={drink.id}>
<h5>{drink.name}</h5>
<h6>${drink.price}</h6>
<span
onClick={() => this.setState({ show: true })}
>
<strong>More Info</strong>
<br />
<button onClick={() => this.addToCart(drink)}>Add to Order</button>
</span>
<Modal
show={this.state.show}
onHide={this.handleHide}
container={this}
aria-labelledby="contained-modal-title"
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title">
{drink.name}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{drink.sub_category} | ABV {drink.abv}% | {drink.origin}</p>
<Col xs={6} md={4}>
<Image className="drink-logo" src={drink.logo} thumbnail />
</Col>
<p className="drink-description"><strong>Description</strong><br />{drink.description}</p>
<p href={drink.website}>Website</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleHide}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
})
return (
<div>
<h2>Drink Menu</h2>
<div>
{drinkList}
</div>
</div>
)
}
}
From the code you have shared, I see that you are handling all the Model with the same state value, i.e. show. This is causing all the state for all the Models to be true hence all of them as shown.
To solve this, you can extract your whole component in a new React class which has just the functionality to show Modal as per the independent state. So your new React component will look something like this:
class DrinkComponent extends React.Component {
constructor(props) {
super(props);
this.handleHide = this.handleHide.bind(this);
this.state = {
show: false,
}
}
handleHide() {
this.setState({ show: false });
}
render() {
const { drink } = this.props;
return (<div key={drink.id}>
<h5>{drink.name}</h5>
<h6>${drink.price}</h6>
<span
onClick={() => this.setState({ show: true })}
>
<strong>More Info</strong>
<br />
<button onClick={() => this.props.addToCart(drink)}>Add to Order</button>
</span>
<Modal
show={this.state.show}
onHide={this.handleHide}
container={this}
aria-labelledby="contained-modal-title"
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title">
{drink.name}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{drink.sub_category} | ABV {drink.abv}% | {drink.origin}</p>
<Col xs={6} md={4}>
<Image className="drink-logo" src={drink.logo} thumbnail />
</Col>
<p className="drink-description"><strong>Description</strong><br />{drink.description}</p>
<p href={drink.website}>Website</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleHide}>Close</Button>
</Modal.Footer>
</Modal>
</div>);
}
}
In this case, each DrinkComponent will have its independent state of showing and hiding of the model. Now we have to just modify your existing render function in DrinkMenu, to display DrinkComponent. So your render function will look something like this:
render() {
let drinkList = this.props.drinkMenu.map((drink) => (<DrinkComponent drink={drink} addToCart={this.addToCart}/>));
return (
<div>
<h2>Drink Menu</h2>
<div>
{drinkList}
</div>
</div>
)
}
Also you can remove the show state from DrinkMenu as it wont be needed there.
Hope it helps.
I'm new to React and to coding in general. I'm trying to render multiple modals in the same component, but they are all being rendered at the same time so that it looks like all the links are rendering the text in the last modal.
Here's where the state is set:
class Header extends React.Component {
constructor () {
super();
this.state = {open:false}
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
this.handleModalChangeEnter = this.handleModalChange.bind(this, true);
this.handleModalChangeLogin = this.handleModalChange.bind(this, false);
}
openModal () {
this.setState({open: true}); }
closeModal () {
this.setState({open: false}); }
render() {
And here's the modal construction:
return (
<header style={home}>
<div style={hello}>
<img style={logo} src='public/ycHEAD.png'/>
<p style={slogan}>One Calendar for All of Yerevan's Tech Events</p>
</div>
<div style={subContainer}>
<ul style={modalDirectory}>
<Button onClick={this.openModal}
style={openButton}>
<li><a style={tabs}>Enter
</a></li>
</button>
<Modal style={modalCont}
isOpen={this.state.open}>
<button onClick={this.closeModal}
style={xButton}>x</button>
</Modal>
<button onClick={this.openModal}
style={openButton}>
<li><a style={tabs}>Login
</a></li>
</button>
<Modal style={modalCont}
isOpen={this.state.open}>
<p>Account</p>
<button onClick={this.closeModal}
style={xButton}>x</button>
</Modal>
Should there be a value in the empty parentheses -> openModal() & closeModal() ?
A friend helped me out with this one. The top half of code remains the same, what changes is in the modal construction (some really helpful aesthetic changes were also made to the 'html'):
return (
<header style={home}>
<div style={hello}>
<img style={logo} src='public/ycHEAD.png'/>
<p style={slogan}>One Calendar for All of Yerevan's Tech Events</p>
</div>
<div style={subContainer}>
<ul style={modalDirectory}>
<li style={tabs}>
<button
onClick={() => this.openModal('login')}
style={openButton}>
Enter
</button>
</li>
<li style={tabs}>
<button
onClick={() => this.openModal('calendar')}
style={openButton}>
Calendar
</button>
</li>
<li style={tabs}>
<button
onClick={() => this.openModal('team')}
style={openButton}>
Meet Us
</button>
</li>
</ul>
</div>
<Modal
style={modalCont}
isOpen={this.state.activeModal === 'login'}>
<p>1!</p>
<button onClick={this.closeModal}
style={xButton}>x</button>
</Modal>
<Modal
style={modalCont}
isOpen={this.state.activeModal === 'calendar'}>
<p>2!</p>
<button onClick={this.closeModal}
style={xButton}>x</button>
</Modal>
<Modal
style={modalCont}
isOpen={this.state.activeModal === 'team'}>
<p>3!</p>
<button onClick={this.closeModal}
style={xButton}>x</button>
</Modal>
</header>
If anyone else can provide a thorough explanation, please do so! Also, there is another way to do this using 'bind', but I don't know how.
I have created similar approach but more detailed model for those who need this solution when using "react-modal". It's not clear in question above if react-modal was used or not because import section is missing, but seems to have references to it. For those who are looking for solution using react-modal to display multiple modals in same component, so here is solution and demo :
import React from "react";
import Modal from "react-modal";
class MutipleButtonsWithModalInSameComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
showModal: false,
activeModal: "",
};
this.handleOpenModal = this.handleOpenModal.bind(this);
this.handleCloseModal = this.handleCloseModal.bind(this);
}
handleOpenModal(val) {
this.setState({ activeModal: val });
this.setState({ showModal: true });
}
handleCloseModal() {
this.setState({ showModal: false });
this.setState({ showModal: "" });
}
render() {
return(
<>
{/* {'one item with modal link -login'} */}
<div className="icon">
<a
className="button"
onClick={() => this.handleOpenModal("login")}
>
login (modal popup)
</a>
<Modal
isOpen={
this.state.showModal &&
this.state.activeModal === "login"
}
contentLabel="login Modal"
>
<div className="content">
<button className="close" onClick={this.handleCloseModal}>X</button>
<p>login content in here</p>
</div>
</Modal>
</div>
{/* {'another item with modal link calendar, add more by mutiplying this below'} */}
<div className="icon">
<a
className="button"
onClick={() => this.handleOpenModal("calendar")}
>
calendar (modal popup)
</a>
<Modal
isOpen={
this.state.showModal &&
this.state.activeModal === "calendar"
}
contentLabel="calendar Modal"
>
<div className="content">
<button className="close" onClick={this.handleCloseModal}>X</button>
<p>calendar content in here...</p>
</div>
</Modal>
</div>
{/* {'another item with modal link team, add more by mutiplying this below'} */}
<div className="icon">
<a
className="button"
onClick={() => this.handleOpenModal("team")}
>
team (modal popup)
</a>
<Modal
isOpen={
this.state.showModal &&
this.state.activeModal === "team"
}
contentLabel="team Modal"
>
<div className="content">
<button className="close" onClick={this.handleCloseModal}>X</button>
<p>team content in here...</p>
</div>
</Modal>
</div>
</>
)
}
}
export default MutipleButtonsWithModalInSameComponent;
Here is a stackblitz link or demo
You can do it in two ways.
1) Simple but doesn't scale: Maintain different state variables and functions for each Modal. i.e.,
this.state = {openModal1:false, openModal2:false}
this.openModal1 = this.openModal1.bind(this);
this.closeModal1 = this.closeModal1.bind(this);
this.openModal2 = this.openModal2.bind(this);
this.closeModal2 = this.closeModal2.bind(this);
As we can see, the problem with this is redundancy of the code.
2) Use functions to eliminate redundancy: Maintain a function to change the content of the modal.
class Header extends React.Component {
constructor () {
super();
this.state = {open:false, ModalContent:''}
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
this.handleModalChangeEnter = this.handleModalChange.bind(this);
this.handleModalChangeLogin = this.handleModalChange.bind(this);
}
openModal () {
this.setState({open: true}); }
closeModal () {
this.setState({open: false}); }
handleModalChange1() {
this.setState({ ModalContent : '<h1>Modal1 Content</h1>'
}
handleModalChange2() {
this.setState({ ModalContent : '<h1>Modal2 Content</h1>'
}
render() {
Modal Construction Should be :
return (
<header style={home}>
<div style={hello}>
<img style={logo} src='public/ycHEAD.png'/>
<p style={slogan}>One Calendar for All of Yerevan's Tech Events</p>
</div>
<div style={subContainer}>
<ul style={modalDirectory}>
<button onClick={this.handleModalChange1}
style={openButton}>
<li><a style={tabs}>Enter
</a></li>
</button>
<button onClick={this.handleModalChange2}
style={openButton}>
<li><a style={tabs}>Login
</a></li>
</button>
<Modal style={modalCont}
isOpen={this.state.open}>
<div dangerouslySetInnerHTML={{__html: this.state.ModalContent}} />
<button onClick={this.closeModal}
style={xButton}>x</button>
</Modal>