I'm new to React so bear with me.
I'm trying to create a modal component that will be triggered from a onClick() function from any global element i.e: link, button, span or whatever throughout the whole app.
import React from 'react';
import ReactDOM from 'react-dom';
const display = {
display: 'block'
};
const hide = {
display: 'none'
};
class Modal extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
toggle: false
}
}
toggle(event) {
this.setState(prevState => ({
toggle: !prevState.toggle
}));
}
render() {
return (
<div>
<button onClick={this.toggle}>Show Modal</button>
<div className="modal" style={this.state.toggle ? display : hide} >
<div className="modal-content">
{this.props.children}
<button onClick={this.toggle}>Close</button>
</div>
</div>
</div>
);
}
}
module.exports = Modal;
You can use react-bootstrap-modal:
npm install --save react-bootstrap-modal
Then in your component:
import React from 'react';
import Modal from 'react-bootstrap-modal';
export default class ModalExample extends React.Component {
constructor(props){
super(props);
this.state = {
open: false,
}
}
openModal = () => this.setState({ open: true })
closeModal = () => this.setState({ open: false })
render(){
return (
<div>
<button type='button' onClick={this.openModal}>Launch modal</button>
<Modal
show={this.state.open}
onHide={this.closeModal}
aria-labelledby="ModalHeader"
>
<Modal.Header>
<Modal.Title id='ModalHeader'>A Title Goes here</Modal.Title>
<div onClick={this.closeModal}>CLOSE HERE</div>
</Modal.Header>
<Modal.Body>
<p>Some Content here</p>
</Modal.Body>
<Modal.Footer>
// If you don't have anything fancy to do you can use
// the convenient `Dismiss` component, it will
// trigger `onHide` when clicked
<Modal.Dismiss className='btn btn-default'>Cancel</Modal.Dismiss>
// Or you can create your own dismiss buttons
<button className='btn btn-primary' onClick={this.closeModal}>
CLOSE HERE TOO
</button>
</Modal.Footer>
</Modal>
</div>
)
}
}
For further reference, please go here:
https://github.com/jquense/react-bootstrap-modal
You may also need to include bootstrap-CSS file if necessary! Please post here some errors if any, thanks
Related
I'm building a server-rendered website using Node.js, Express.js, MySQL, and Next.js (framework for React). The concept is basically a Jeopardy game.
Currently what I have is I query from the database and extract all the questions. I then made a table with the $amounts and each cell in the table can be clicked on to reveal the question. When you click on a cell/question in the table, a modal opens up. It says the question and then below it there's a space to answer and a submit button.
Right now what I want to do is when I click the submit button, the current modal with the question should disappear and then a new modal featuring "feedback" basically saying correct with a checkmark or incorrect with a x symbol should pop up.
But I'm not sure how to do the above or even if that's a good way to go about this. I'm very new to this so please help me out.
Here is what I have so far. Right now when I run this code, when I click submit, the question modal disappears but the feedback modal does not pop up.
Question Modal:
import React, { Component } from 'react';
import Modal from "react-bootstrap/Modal";
import Button from "react-bootstrap/Button";
import UserInput from './userInput.js';
import Feedback from "./feedback/feedback";
class SampleQ extends Component {
static getInitialProps({query: {amount, question, answer}}) {
return {specAmt: amount, specQ: question, specA: answer}
}
constructor(props) {
super(props);
this.state = {
showQuestion: false, // tracks visibility of first modal (the question modal in this component)
showFeedback: false // tracks visibility of second modal (the feedback modal in other component)
};
this.handleShow = () => {
this.setState({ showQuestion: true });
};
this.handleHide = () => {
this.setState({ showQuestion: false });
};
this.submitForm = () => {
this.setState({
showQuestion: false, // close question modal
showFeedback: true, // should open Feedback modal
});
};
this.closeFeedback = () => {
this.setState( { showFeedback: false }); // close Feedback modal
}
}
render() {
return (
<>
<Button variant="outline-danger" size = "lg" onClick={this.handleShow}>
$ {this.props.amount}00
</Button>
<Modal
show={this.state.showQuestion}
onHide={this.handleHide}
dialogClassName="modal-90w"
aria-labelledby="example-custom-modal-styling-title"
>
<Modal.Header closeButton>
<Modal.Title id="example-custom-modal-styling-title">
Question
</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>
{this.props.question}
</p>
<div>
<UserInput
answer={this.props.specA}
handleClick={this.submitForm}
/>
<Feedback
showModal={this.state.showFeedback}
onSubmit={this.closeFeedback}
/>
</div>
</Modal.Body>
</Modal>
</>
);
}
}
export default SampleQ;
Form for the Question Modal:
import React, { Component } from "react";
import Form from "react-bootstrap/Form";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import Button from "react-bootstrap/Button";
class UserInput extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Form onSubmit={this.props.handleClick}>
<Form.Group as={Row} controlId="formAnswer">
<Form.Label column sm={2}>
Answer
</Form.Label>
<Col sm={10}>
<Form.Control type="text" placeholder="Enter Here"/>
</Col>
</Form.Group>
<div>
<Button
variant="primary"
onClick={this.props.handleClick}
>Submit</Button>
</div>
</Form>
);
}
}
export default UserInput;
Feedback Modal:
import React, { Component } from 'react';
import styles from "../../scss/modalStyle.scss";
import Modal from "react-bootstrap/Modal";
import { AiFillCheckCircle } from 'react-icons/ai';
import { IconContext } from "react-icons";
import Button from "react-bootstrap/Button";
class Feedback extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Modal
show={this.props.showModal}
onHide={this.props.onSubmit}
className="Feedback"
>
<Modal.Dialog style={[styles.modalDialog, styles.modalConfirm]}>
<Modal.Body style={styles.modalContent}>
<Modal.Body style={styles.modalHeader}>
<Modal.Body style={styles.iconBox}>
<IconContext.Provider value={{ size: "70px", className: "global-class-name" }}>
<div>
<AiFillCheckCircle />
</div>
</IconContext.Provider>
</Modal.Body>
<h4 className='mx-auto'>Congratulations!</h4>
</Modal.Body>
<Modal.Body style={styles.modalBody}>
<p className="text-center">That was the correct answer.</p>
</Modal.Body>
<Modal.Body style={styles.modalFooter}>
<Button
className="btn btn-success btn-block"
data-dismiss="modal"
onClick={this.props.onSubmit}
>
OK
</Button>
</Modal.Body>
</Modal.Body>
</Modal.Dialog>
</Modal>
);
}
}
export default Feedback;
You can handle the second modal the same way you handled the first one:
Add a key to SampleQ state and track the second modal's visibility with it.
Pass a handler changing that visibility to true to UserAnswer and invoke it on submit click.
You should not open a modal "in" another modal like this: {modalOpen ? <Feedback showModal={this._openModal.bind(this)} closeModal={this._closeModal.bind(this)} /> : ''}. SampleQ should store in its state the visibility of both modals.
I have an app with a list of meal, and clicking on a meal should make a modal appear on the screen with further information about the meal.
So I am trying to code a basic modal as a reusable component using the react-modal package.
However when I try to 'activate' the modal it does not work. the openModal method does get fired but the modal does not show up on the screen.
App.js:
import React from 'react';
import MealCard from './MealCard';
import MealModal from './MealModal';
export default class App extends React.Component {
constructor() {
super();
this.state = {
modalIsOpen: false
}
this.openModal = this.openModal.bind(this);
};
openModal() {
this.setState({modalIsOpen: true});
console.log(this.state.modalIsOpen);
}
render() {
return (
<div>
<div className="app-wrapper" style={{display: 'flex'}}>
<div className="container">
<div className="row">
{[...Array(20)].map((x, i) =>
<div className="col-sm-6 col-xs-12 " key={i} onClick={this.openModal}>
<MealCard />
</div>
)}
</div>
</div>
</div>
<MealModal isOpen={this.state.modalIsOpen}/>
</div>
);
}
}
MealModal.js
import React from 'react';
import Modal from 'react-modal';
const customStyles = {
content : {
top : '50%',
left : '50%',
right : 'auto',
bottom : 'auto',
marginRight : '-50%',
transform : 'translate(-50%, -50%)'
}
};
Modal.setAppElement('#app')
export default class MealModal extends React.Component {
constructor(props) {
super(props);
this.state = {
modalId: 0,
modalIsOpen: false
};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
componentWillMount() {
this.setState({modalId: 3})
}
openModal() {
this.setState({modalIsOpen: true});
}
closeModal() {
this.setState({modalIsOpen: false});
}
render() {
return (
<Modal
isOpen={this.props.modalIsOpen}
onRequestClose={this.closeModal}
style={customStyles}
contentLabel="Meal Modal"
>
<div className="modal-wrapper">
<div className="container text-center">
<h1>Hello</h1>
<h2>ID of this modal is {this.state.modalId}</h2>
<h3>This is an awesome modal.</h3>
<button onClick={this.closeModal}>close</button>
</div>
</div>
</Modal>
)
}
}
You're passing isOpen as props and using modalIsOpen (props) in MealModal component.
As mentioned in the comment, you can just use isOpen={this.props.isOpen}. There's no sense to use two states for serving the same purpose, one is modalIsOpen in App component and other is modalIsOpen in MealModel component
I have a Parent component, App.js and a Child component, MealModal.js. When a user click on a specific meal card, it raises a modal that should display further information about the meal.
Hence I try to find a way to dynamically change the modals's data, depending on which meal card is clicked
I have tried to pass the id of the meal to the onClick={this.openModal} function and set the state of modalId is the function. But I got the following error:
Warning: Cannot update during an existing state transition (such as
within render or another component's constructor). Render methods
should be a pure function of props and state; constructor side-effects
are an anti-pattern, but can be moved to 'componentWillMount'.
Here are my components so far:
App.js:
import React from 'react';
import MealCard from './MealCard';
import MealsMap from './MealsMap';
import MealsFilters from './MealsFilters';
import MealModal from './MealModal';
export default class App extends React.Component {
constructor() {
super();
this.state = {
modalIsOpen: false,
modalId: 0
}
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
};
openModal() {
this.setState({modalIsOpen: true});
};
closeModal() {
this.setState({modalIsOpen: false});
};
render() {
return (
<div>
<MealsFilters/>
<div className="app-wrapper" style={{display: 'flex'}}>
<div className="container">
<div className="row">
{[...Array(20)].map((x, i) =>
<div className="col-sm-6 col-xs-12 " key={i} onClick={this.openModal}>
<MealCard />
</div>
)}
</div>
</div>
<MealsMap/>
</div>
<MealModal modalIsOpen={this.state.modalIsOpen} closeModal={this.closeModal} modalId={this.state.modalId}/>
</div>
);
}
}
MealModal.js
import React from 'react';
import Modal from 'react-modal';
const customStyles = {
content : {
}
};
Modal.setAppElement('#app')
export default class MealModal extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Modal
isOpen={this.props.modalIsOpen}
onRequestClose={this.props.closeModal}
style={customStyles}
contentLabel="Meal Modal"
>
<div className="modal-wrapper">
<div className="container text-center">
<h1>Hello</h1>
<h2>ID of this modal is {this.props.modalId}</h2>
<h3>This is an awesome modal.</h3>
<button onClick={this.props.closeModal}>close</button>
</div>
</div>
</Modal>
)
}
}
Any idea on how I could do this ?
Ok so I found the solution:
First, I changed onClick={this.openModal} in the parent comoponent to onClick= () => {this.openModal}
Second, I add the id as a parameter:
onClick= () => {this.openModal(i)}
Finally: update the openModal function:
openModal(modalId) {
this.setState({modalIsOpen: true,
modalId});
};
And it works.
openModal(modalId) {
this.setState({
modalId,
modalIsOpen: true
});
};
and modify the call function as
<div className="col-sm-6 col-xs-12" key={i} onClick={() => this.openModal(x) } >
<MealCard/>
</div>
I can not close current modal when open new modal in React js. please help me.
I have parent modal: Register_modal and child of it: RegisterCode_Modal
parent modal is called in header component:
first: Header component
this component call first modal and pass open and close props to it:
import React , {Component} from 'react';
import ReactDOM from 'react-dom';
import {NavLink} from 'react-router-dom';
import Register_Modal from './Register_Modal';
export default class Header extends Component {
constructor() {
super();
this.state = {
modalIsOpen: false
};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal(e) {
e.preventDefault();
this.setState({modalIsOpen: true});
}
closeModal(e) {
e.preventDefault();
this.setState({modalIsOpen: false});
}
render() {
return (
<div>
<div className="button navbar-right">
<button className="navbar-btn nav-button wow bounceInRight login" data-wow-delay="0.45s">ورود</button>
<button className="navbar-btn nav-button wow fadeInRight" data-wow-delay="0.48s" onClick={this.openModal} >ثبت نام</button>
<div >
<Register_Modal open={this.state.modalIsOpen} close={this.closeModal} />
</div>
</div>
);
}
}
---------------------------------------------------------------------------
second: parent component
export default class Register_Modal extends Component {
constructor(props){
super(props);
this.state={
codemodal: false
};
this.openCodeModal=this.openCodeModal.bind(this);
this.closeCodeModal=this.closeCodeModal.bind(this);
}
openCodeModal(e){
e.preventDefault();
this.setState({codemodal: true});
}
closeCodeModal(e){
e.preventDefault();
this.setState({codemodal: false});
}
render() {
return (
<div>
<Modal
isOpen={this.props.open}
onRequestClose={this.props.close}
ariaHideApp={false}
contentLabel="selected option"
isClose={this.props.close}
style={customStyles}
>
<h2>salammmmm</h2>
<button onClick={this.props.close} >انصراف</button>
<button onClick={this.openCodeModal} >بعدی</button>
</Modal>
<div className="ReactModalPortal">
<RegisterCode_Modal open={this.state.codemodal} close={this.closeCodeModal} />
</div>
{this.props.close}
</div>
);}
}
------------------------------------------------------------------
third: child component
export default class RegisterCode_Modal extends Component {
constructor(props){
super(props);
console.log("injaaaaa");
}
render() {
return (
<div>
<Modal
isOpen={this.props.open}
onRequestClose={this.props.close}
ariaHideApp={false}
contentLabel="ورود کد"
isClose={this.props.close}
style={customStyles}
>
<h2>مرحله کد</h2>
<button onClick={this.props.close} >تائید</button>
</Modal>
</div>
);}
}
You can simply achieve this by rendering them conditionally.
I personally so this:
export default class RegisterModal extends Component {
state = {
showBaseModal: true,
codemodal: false,
};
openCodeModal = () => {
this.setState({
codemodal: true,
showBaseModal: false,
});
};
closeCodeModal = () => {
this.setState({ codemodal: false });
};
render() {
return (
<div>
{this.state.showBaseModal && (
<Modal
isOpen
onRequestClose={this.props.close}
ariaHideApp={false}
isClose={this.props.close}
>
<button onClick={this.props.close}>Close</button>
<button onClick={this.openCodeModal}>Next</button>
</Modal>
)}
{this.state.codemodal && (
<RegisterCode_Modal
open={this.state.codemodal}
close={this.closeCodeModal}
/>
)}
</div>
);
}
}
Adding an extra state for base modal. On openCodeModal event, toggle it to false to stop both modals.
Set isOpen always to true for both modals, and then render RegisterModal component conditionally.
Trying to learn React and Reactstrap and trying to get Modals to work. When I click the button, it should toggle the Modal, but right now it's giving me this error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of 'App'
Can't figure out what I'm doing wrong. Can someone help let me know where I'm going wrong here? Appreciate any help you can give.
I'd like to use the most recent versions of React and Reactstrap if possible.
Here's a link on Codepen: https://codepen.io/lieberscott/pen/ddYNVP
const { Button,
Container,
Modal,
ModalTitle,
ModalHeader,
ModalBody,
ModalFooter } = Reactstrap;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showModal: false
}
this.toggleModal = this.toggleModal.bind(this);
}
toggleModal() {
console.log("hello");
this.setState({
showModal: !this.state.showModal
})
}
render() {
return (
<Container>
<Headline />
<Box />
<Button outline color="primary" onClick={this.toggleModal}>Click</Button>
<Modal isOpen={this.state.showModal} toggle={this.toggleModal} className="modal">
<ModalHeader>
<ModalTitle id="modalTitle">
Add a Recipe
</ModalTitle>
</ModalHeader>
<ModalBody>
Modal body
</ModalBody>
<ModalFooter>
Modal footer
</ModalFooter>
</Modal>
</Container>
);
}
}
class Headline extends React.Component {
render() {
return (
<div>
Recipes
</div>
);
}
}
class Box extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [
{
name: "Tofu tacos",
ingredients: [
"Shells",
"lettuce",
"tofu",
"paprika"
]
},
{
name: "Spaghetti",
ingredients: [
"pasta",
"sauce",
"salt"
]
}
] // end of items
} // end of this.state
} // end of constructor
render() {
const allitems = this.state.items.map(item => {
return (
<div>
{item.name}
</div>
);
})
return (
<div>
{allitems}
</div>
);
}
}
const app = document.getElementById("app");
ReactDOM.render(<App />, app);
I don't know how to share my code via codepen like you did, sorry.
I did these changes:
toggleModal() {
console.log("hello");
console.log( 'before setState: ', this.state );
this.setState({
showModal: !this.state.showModal
})
console.log( 'after setState: ', this.state );
}
The Button onClick event, when you do onClik={this.something} this this refers to the Button element. I changed to:
<Button outline color="primary" onClick={() => this.toggleModal()}>Click</Button>
When I do onClick={ () => this.something() } allows me to make use of the methods of my class.
Just look at the console.log output and you'll see clicking on button change showModal to true or false.
Take a look on cephalization's answer: https://forum.freecodecamp.org/t/difference-in-reactjs-onclick-function-binding/116027/2
It is far easier this way
add your state in the constructor example
this.state={showLogOut: false}
in your return all you need is to setState true example
<Button onClick={() => this.setState({showLogOut: true})}>Show modal</Button>
now all we need is the modal code since we have an state and a setState
<Modal isOpen={this.state.showLogOut} fade="false" toggle={() => this.setState({showLogOut: false})}>
<ModalHeader toggle={() => this.setState({showLogOut: false})}>Ready to leave?</ModalHeader>
<ModalBody>
<p>Press logout to end session.</p>
</ModalBody>
<ModalFooter>
<Button onClick={() => this.setState({showLogOut: false})}>Cancel</Button>
<Button color="info">Logout</Button>
</ModalFooter>
this should be enough to show your modal: I am assuming you are using react-strap and that you are importing the required elements but just to make sure here is what you need to import
import { Button, Modal, ModalBody, ModalFooter, ModalHeader} from 'reactstrap';
Reactstrap Modal don't show, because is need fade set on False. plz look my code who worked.
class Demoextends extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
fade: false
};
this.toggle = this.toggle.bind(this);
};
toggle() {
console.log("hello");
this.setState({
modal: !this.state.modal
});
console.log( 'after setState: ', this.state );
}
render() {
return (
<div>
<Button color="danger" onClick={this.toggle}>Launch</Button>
<Modal isOpen={this.state.modal} fade={this.state.fade } toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>Modal title</ModalHeader>
<ModalBody>
</ModalBody>
<ModalFooter>
<Button onClick={this.toggle}>Do Something</Button>{' '}
<Button onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default Demoextends;
Of course You must add and part of reactstrap in your code