How to hide react bootstrap modal and also call the save function? - reactjs

OnClick event, onClick={this.props.onHide} I'm being able to hide the Modal but I also need to call the save function. If I call the save function along with the props then the save function is called but the Modal doesn't close. Any help is appreciated.
Component 1 -
import * as React from 'react';
import Modal from '../Modal/Modal';
export default class Example extends React.Component<IExampleProps, {}>{
constructor(public props: IAboutProps, public state: any) {
super(props);
this.state = {
helpModalShow: false,
};
}
public render(): React.ReactElement<IExampleProps> {
let helpModalClose = () => this.setState({ helpModalShow: false });
return (
<Modal
show={this.state.helpModalShow}
onHide={helpModalClose}
/>
);
}
}
Modal Component -
import * as React from 'react';
export default class Modal extends React.Component<IModalProps, {}>{
constructor(public props: IModalProps, public state: any) {
super(props);
this.state = {
};
}
public render(): React.ReactElement<IQuestionsModalProps> {
return (
<Modal
{...this.props}
//size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title>
Ask a Question
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Row>
<Col>
<button type="button" onClick=
{this.props.onHide}>Submit</button>
</Col>
</Row>
</Modal.Body>
</Modal>
);
}
}

then this should work call a function in onClick like this
save = () => {
//your save logic
this.props. onHide()
}
<Col>
<button type="button" onClick=
{this.save}>Submit</button>
</Col>

Related

Not able to figure out why state is empty while writing unit tests in Jest

I've a react component which I'm trying to unit test via Jest.
My component looks like this:
export class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
expandModal: false
};
this.setExpandModal = this.setExpandModal.bind(this);
}
setExpandModal(expandModal) {
this.setState({expandModal});
}
render() {
const {expandModal} = this.state;
return (
<Container>
<div>
<Modal
show={expandModal}
onHide={() => this.setExpandModal(false)}
>
<Modal.Header>
<Modal.Title> Title </Modal.Title>
</Modal.Header>
<Modal.Body>
<div>Some body!</div>
</Modal.Body>
</div>
<Row>
<Col>
<Button id={"show-modal-button"}
onClick={() => this.setExpandModal(true)}
>Show Modal
</Button>
</Col>
</Row>
</Container>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
My test file:
it("closing modal sets state to false", function () {
const wrapper = mount(<Provider store={store}>
<MyComponent/>
</Provider>
);
const button = wrapper.find("#show-modal-button").first();
button.simulate("click");
const modal = wrapper.find(Modal);
expect(modal.prop("show")).toBe(true);
modal.invoke("onHide")();
expect(wrapper.state("expandModal")).toBe(false);
});
I get an error saying that state shouldn't be null or undefined. When I log inside MyComponent it shows the state does get updated, but I'm not able to figure out why my tests aren't able to get
the updated state. I tried using wrapper.update() but that didn't help either.

Opening a Modal in React with an ID passed to the component

I have an application which prompts a user to add an entry via a modal. However, I can't figure out how to call the modal from the function and neither can I figure out how to pass the id to the modal.
So far I've been able to get the two parts written up and compiled but not working together
'Add Entry' button
import React from 'react';
import '../../App.css'
import VideoModal from '../components/VideoModal'
function NewEntry ({event}){
return (
<span><i class="fa fa-plus-circle" aria-hidden="true"></i></span>
<VideoModal entry={event}
);
}
export default NewEntry;
Note that this doesn't call the modal component. I've attempted variations of onClick={this.closeModal} for the <I> tag to no avail, but this obviously won't work. And I've included the modal as a component.
I know, I know this isn't the right way of doing this, I just haven't yet found examples I can wrap my head around.
The Modal
import React, { Component } from "react";
import { Modal, Button } from "react-bootstrap";
import '../../App.css'
class VideoModal extends Component {
state = {
isOpen: false
};
openModal = () => this.setState({ isOpen: true });
closeModal = () => this.setState({ isOpen: false });
render() {
return (
<>
<Modal show={this.state.isOpen} onHide={this.closeModal}>
<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={this.closeModal}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
}
}
export default VideoModal;
First of all, the VideoModal tag is not closed, pay attention to it.
Then you can move the open/close logic outside the modal, inside NewEntry:
import React, { Component } from 'react';
import '../../App.css'
import VideoModal from '../components/VideoModal'
class NewEntry extends Component {
state = {
isModalOpen: false
};
openModal = () => this.setState({ isModalOpen: true });
closeModal = () => this.setState({ isModalOpen: false });
render() {
return (
<span onClick={() => this.openModal()}><i class="fa fa-plus-circle" aria-hidden="true"></i></span>
<VideoModal closeModal={() => this.closeModal()} isOpen={this.state.isOpen} />
);
}
}
export default NewEntry;
And use VideoModal as a pure component:
import React from "react";
import { Modal, Button } from "react-bootstrap";
import '../../App.css'
function VideoModal (props) {
const { isOpen, closeModal } = props;
return (
<>
<Modal show={isOpen} onHide={closeModal}>
<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={closeModal}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
}
export default VideoModal;

Modal Component - How to include and open/close from another component

I'm just starting to learn React. I've got a modal component (that's basically a wrapper for react-bootstrap's Modal component). The idea is to have a "Feedback" modal that I can include in various places. This approach isn't working, and I don't know what I don't know :/
Below is a quick example of what I mean / how I'm trying to display my modal component
import React, { Component } from 'react'
import Modal from 'react-bootstrap/Modal'
import Button from "components/button/button"
export class BaseModal extends Component {
constructor(props) {
super(props);
this.state = { show: false };
}
toggleModal() {
this.setState({ show: !this.state.show })
}
render() {
if (!this.props.show) {
return null;
};
return (
<>
<Modal show={this.state.show}>
<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={this.toggleModal}>
Close
</Button>
<Button variant="primary" onClick={this.toggleModal}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</>
)
}
}
A simple page like this - clicking this button doesn't do anything (React dev tools show no BaseModal node in the vdom)
import React, { Component } from 'react'
import Button from "components/button/button"
import BaseModal from "components/modals/baseModal"
export class ButtonDocs extends Component {
render() {
<Button value="Open Modal" onClick={BaseModal.toggleModal} />
}
}
You can't just import a Component and then call a method on it, because you aren't actually rendering it anywhere.
What you need to do is render the Component, and then if you want to control the state of one component from another you need to "lift state up" and pass the state and any methods needed to the modal component as props. Something like this:
Modal Component
import React, { Component } from 'react'
import Modal from 'react-bootstrap/Modal'
import Button from "components/button/button"
export class BaseModal extends Component {
render() {
if (!this.props.show) {
return null;
};
return (
<>
<Modal show={this.state.show}>
<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={this.props.toggleModal}>
Close
</Button>
<Button variant="primary" onClick={this.props.toggleModal}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</>
)
}
}
Button Docs
import React, { Component } from 'react'
import Button from "components/button/button"
import BaseModal from "components/modals/baseModal"
export class ButtonDocs extends Component {
constructor(props) {
super(props);
this.state = { show: false };
}
toggleModal() {
this.setState({ show: !this.state.show })
}
render() {
<Button value="Open Modal" onClick={this.toggleModal} />
<BaseModal show={this.state.show} toggleModal={this.toggleModal} />
}
}

How to customize react-bootstrap modal

Suppose I have a modal like as follows and I would like it such that, when the modal is showing at that time I also want to work on the background.
In the following code, there is a textbox. I want to work with the textbox(or an audio) when the model is appearing.
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { Modal, Button } from 'react-bootstrap';
import './GenericModal.scss';
class GenericModal extends Component {
constructor(props, context) {
super(props, context);
this.state = {
showModal: false
};
this.open = this.open.bind(this);
this.close = this.close.bind(this);
}
open() {
this.setState({showModal: true});
}
close() {
this.setState({showModal: false});
}
render() {
return(
<div>
<div>I am a Bootstrap Modal</div>
<Button onClick={this.open}>Show Modal</Button>
<div>
<Modal className="modal-container" id="demo-class" ref={node => this.chart = node}
show={this.state.showModal}
onHide={this.close}
bsSize="small" backdrop={false}
>
<Modal.Header closeButton>
<Modal.Title>Modal title</Modal.Title>
</Modal.Header>
<Modal.Body>
One of fine body.........
</Modal.Body>
<Modal.Footer>
<Button onClick={this.close}>Close</Button>
<Button bsStyle="primary">Save changes</Button>
</Modal.Footer>
</Modal>
<Button onClick={this.open}>Show Modal</Button>
<input type="text" />
</div>
</div>
);
}
}
export default GenericModal;
I hope the below example flow addresses your requirement.
More form the comments in code.
i.e onClick calls wrapper function which has open method for modal together with your customizations.
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { Modal, Button } from 'react-bootstrap';
import './GenericModal.scss';
class GenericModal extends Component {
constructor(props, context) {
super(props, context);
this.state = {
showModal: false
};
this.open = this.open.bind(this);
this.close = this.close.bind(this);
}
open() {
let _this = this;
this.setState({showModal: true}, function(){
//state is set, i.e modal is loaded in view
//here you can do whatever like stopping audio
//_this.stopAudio();
});
}
close() {
this.setState({showModal: false});
}
playSomeAudio(){
//playAudio();
}
stopAudio(){
//stop the audio
}
doSomethingBeforeOpen(){
var _this = this;
//do whatever you want before opening model. i.e palying audio
//1. in sync:
this.playSomeAudio(); //audio starts palying before modal starts triggered
//2. in async
setTimeOut(() => {_this.playSomeAudio()}, 1); //async by setTimeout, keep your own time
//Or any
this.open(); //opens modal
}
render() {
return(
<div>
<div>I am a Bootstrap Modal</div>
<Button onClick={this.doSomethingBeforeOpen.bind(this)}>Show Modal</Button>
<div>
<Modal className="modal-container" id="demo-class" ref={node => this.chart = node}
show={this.state.showModal}
onHide={this.close}
bsSize="small" backdrop={false}
>
<Modal.Header closeButton>
<Modal.Title>Modal title</Modal.Title>
</Modal.Header>
<Modal.Body>
One of fine body.........
</Modal.Body>
<Modal.Footer>
<Button onClick={this.close}>Close</Button>
<Button bsStyle="primary">Save changes</Button>
</Modal.Footer>
</Modal>
<Button onClick={this.doSomethingBeforeOpen.bind(this)}>Show Modal</Button>
<input type="text" />
</div>
</div>
);
}
}
export default GenericModal;

Cannot read property 'setState' of null - React.js, Modal, Bootstrap

I am trying React.js for the first time in trying to build this web app. I am getting Cannot read property 'setState' of null on the specified line below. I've been trying to figure out why for the past few hours and cannot seem to figure it out. Any help would be greatly appreciated.
MyModal.jsx
import React from 'react';
import { Modal, Button, ButtonToolbar } from 'react-bootstrap';
export default class MyModal extends React.Component {
constructor(props) {
super(props);
this.state = {show: false};
}
showModal() {
this.setState({show: true});
}
hideModal() {
this.setState({show: false});
}
render() {
return (
<ButtonToolbar>
<Button bsStyle="primary" onClick={this.showModal}>
Launch demo modal
</Button>
<Modal
{...this.props}
show={this.state.show}
onHide={this.hideModal}
dialogClassName="custom-modal"
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-lg">Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>Wrapped Text</h4>
<p>Blah</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.hideModal}>Close</Button>
</Modal.Footer> // Chrome inspector says it errors on this line
</Modal>
</ButtonToolbar>
);
} // end render function
} // end export default
bind your component class methods inside the constructor() (binding inside constructor is better than binding inside render() for performance sake):
constructor(props) {
super(props);
this.state = {show: false};
this.showModal = this.showModal.bind(this);
this.hideModal = this.hideModal.bind(this);
}

Resources