Semantic UI modal onOpen/onClose not working - javascript

I want to let the modal close itself after 3000ms.
But the onOpen callback is not called at all when the modal is open.
Is there a workaround for this?
I put console.log in onOpen and onClose, but nothing is appearing in the console.
import React, { Component } from 'react'
import { Modal, Button, Header } from 'semantic-ui-react'
export default class YourTurnModal extends Component {
constructor(props) {
super(props)
this.state = {
modalState: this.props.isYourTurn,
}
this.handleOpen = this.handleOpen.bind(this)
this.handleClose = this.handleClose.bind(this)
}
componentWillReceiveProps(nextProps) {
this.setState({ modalState: nextProps.isYourTurn })
}
render() {
return (
<div>
<Modal
onOpen={() => {
console.log("Open", this.state.modalState);
this.setState({
modalState: true
},()=>{
setTimeout(() => {this.setState({ modalState: false });}, 3000);});
}}
open={this.state.modalState}
onClose={() => {this.setState({ modalState: false });console.log("here")}}
>
<Modal.Content style={{ borderless: 'true' }}>
<Header>
Your turn!
</Header>
<Button
color="green"
onClick={() => {this.setState({ modalState: false })}}>
close
</Button>
</Modal.Content>
</Modal>
</div>
)
}
}
I tried with:
<YourTurnModal isYourTurn={true} />

Related

How do I refactor the modal into a separate file?

Sorry, React developer newbie. I have a working React Class Component with a popup Modal. It displays when you click on a Item Card:
import React from 'react';
import {
Card, Button, CardImg, CardTitle, CardText, CardGroup,
CardSubtitle, CardBody, CardFooter, CardHeader, CardColumns, CardDeck
} from 'reactstrap';
import Config from 'config';
import { decompressToBase64, formatter } from './common.js'
import "./Item.css";
import Modal from 'react-modal';
const customStyles = {
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
},
};
Modal.setAppElement('#root');
class FeaturedCards extends React.Component {
constructor() {
super();
this.state = {
name: 'React',
apiData: [],
isOpen: false
};
}
async componentDidMount() {
const tokenString = sessionStorage.getItem("token");
const token = JSON.parse(tokenString);
let headers = new Headers({
"Accept": "application/json",
"Content-Type": "application/json",
'Authorization': 'Bearer ' + token.token
});
const response = await fetch(Config.apiUrl + `/api/Items/GetFeaturedItems`, {
method: "GET",
headers: headers
});
const json = await response.json();
console.log(json);
this.setState({ itemList: json });
}
render() {
const items = this.state.itemList;
let subtitle;
handleClick = handleClick.bind(this);
closeModal = closeModal.bind(this);
function handleClick() {
this.setState({ isOpen: true });
}
function closeModal() {
console.log('Clicked close button')
this.setState({ isOpen: false });
}
function afterOpenModal() {
// references are now sync'd and can be accessed.
subtitle.style.color = '#f00';
}
return (
<div>
<CardGroup className="card-group-scroll">
{items && items.map(item =>
<>
<Card key={item.itemNumber} tag="a" onClick={() => handleClick()} style={{ cursor: "pointer" }}>
<CardHeader tag="h3">Featured</CardHeader>
<CardImg top className="card-picture" src={"data:image/png;base64," + decompressToBase64(item.images[0]?.compressedImageData)} id={item.itemNumber + "Img"} alt={item.itemNumber} />
<CardBody className="card-body">
<CardTitle tag="h5">{item.itemNumber}</CardTitle>
<CardSubtitle tag="h6" className="mb-2 text-muted">{item.categoryName}</CardSubtitle>
<CardText className="card-description">{item.itemDescription}</CardText>
</CardBody>
<CardFooter className="text-muted">{formatter.format(item.price)}</CardFooter>
</Card>
<Modal
isOpen={this.state.isOpen}
onAfterOpen={afterOpenModal}
onRequestClose={() => closeModal()}
style={customStyles}
contentLabel={item.itemNumber}>
<h2 ref={(_subtitle) => (subtitle = _subtitle)}>Hello</h2>
<button onClick={() => closeModal()}>close</button>
<div>I am a modal</div>
<form>
<input />
<button>tab navigation</button>
<button>stays</button>
<button>inside</button>
<button>the modal</button>
</form>
</Modal>
</>
)}
</CardGroup>
</div>
);
}
}
export default FeaturedCards;
I need to use this same modal in several different places. I need the modal to have access to the item I clicked on. How do I refactor the modal into a separate file?
Any help would be appreciated.
UPDATE
My first attempt:
import React from 'react';
import "./Item.css";
import Modal from 'react-modal';
const customStyles = {
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
},
};
Modal.setAppElement('#root');
class ItemModal extends React.Component {
constructor(props) {
super(props);
}
render() {
let subtitle;
closeModal = closeModal.bind(this);
function closeModal() {
console.log('Clicked close button')
this.setState({ isOpen: false });
}
function afterOpenModal() {
// references are now sync'd and can be accessed.
subtitle.style.color = '#f00';
}
return (
<Modal
isOpen={this.state.isOpen}
onAfterOpen={afterOpenModal}
onRequestClose={() => closeModal()}
style={customStyles}
contentLabel={this.props.item.itemNumber}>
<h2 ref={(_subtitle) => (subtitle = _subtitle)}>Hello</h2>
<button onClick={() => closeModal()}>close</button>
<div>I am a modal</div>
<form>
<input />
<button>tab navigation</button>
<button>stays</button>
<button>inside</button>
<button>the modal</button>
</form>
</Modal>
);
}
}
export default ItemModal;
I call it from FeaturedCards like this <ItemModal item = { item } />
I get an error: TypeError: this.state is null. I can't seem to figure out how to read(Share) the state from FeaturedCards...
Here is the very basic(with only open and close functionalities) example of how you can create a common modal (there could be other ways...).
class ItemModal extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Modal
isOpen
onRequestClose={this.props.onRequestClose}
>
Sample content
</Modal>
);
}
}
export default ItemModal;
and this can be used like below.
class FeaturedCards extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
}
onRequestClose = () => {
this.setState({isOpen: false});
}
handleClick = () => {
this.setState({ isOpen: true });
}
render() {
const { isOpen } = this.state;
return (
<div>
<button type="button" onClick={this.handleClick}> open Modal</button>
{ isOpen ? <ItemModal onRequestClose={this.onRequestClose} /> : null }
</div>
);
}
}
export default FeaturedCards;

How can I make the Snackbar work in a class component?

These are my codes for the snackbar and it wasn't working whenever I'll click the button. I wanted the snackbar to appear once I'll click the button "confirm". Almost all of the examples I have seen are in a functional component, so how can I make the Snackbar work as expected in a class component?
class name extends Component {
constructor() {
super();
this.state = { orders: [], open: false };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
columns = [
{
name: "Confirm",
options: {
customBodyRender: (value, tableMeta) => {
return (
<FormControlLabel
value={value}
control={
<Button>
confirm
</Button>
}
onClick={(e) => {
try {
//firestore codes
);
} catch (err) {
console.log(err);
}
this.handleOpen();
}}
/>
);
},
},
},
];
//code for options
//data fetching codes
render() {
const { open } = this.state;
return this.state.orders ? (
<div>
//muidatatable codes
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
open={open}
onClose={this.handleClose}
autoHideDuration={2000}
// other Snackbar props
>
Order Confirmed
</Snackbar>
</div>
) : (
<p>Loading...</p>
);
}
}
Ignoring a few syntax errors, you should check if there are any orders by using the length and not just by mere existence of the array as you have initialized an empty array this.state.orders will always result in true. Instead use this.state.orders.length > 0 ? to check if there are any orders or not.
Snackbar's child(ren) should be wrapped in components and not just strings directly, for using string directly you can use message prop of Snackbar.
Also, it's a standard to write class's name starting with an upper-case letter.
Here's a working code: Material UI Snackbar using classes
import React, { Component } from "react";
import { FormControlLabel, Button, Snackbar } from "#material-ui/core";
import MuiAlert from "#material-ui/lab/Alert";
export default class Name extends Component {
constructor() {
super();
this.state = { orders: [], open: false };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
handleClick = () => this.setState({ orders: [1], open: true });
columns = [
{
name: "Confirm",
options: {
customBodyRender: (value, tableMeta) => {
return (
<FormControlLabel
value={value}
control={<Button>confirm</Button>}
onClick={(e) => {
try {
//firestore codes
} catch (err) {
console.log(err);
}
this.handleOpen();
}}
/>
);
}
}
}
];
//code for options
//data fetching codes
render() {
const { open } = this.state;
return (
<>
<Button variant="outlined" onClick={this.handleClick}>
Open snackbar
</Button>
{this.state.orders.length > 0 ? (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
open={open}
onClose={this.handleClose}
autoHideDuration={2000}
// other Snackbar props
>
{/* <span
style={{
background: "#000",
color: "#fff",
padding: "20px 5px",
width: "100%",
borderRadius: "5px"
}}
>
Order Confirmed
</span> */}
<MuiAlert
onClose={this.handleClose}
severity="success"
elevation={6}
variant="filled"
>
Success Message
</MuiAlert>
</Snackbar>
</div>
) : (
<p>loading...</p>
)}
</>
);
}
}
The following changes are made to make it work:
Removed Order Confirmed and used message prop of Snackbar
Passed values to orders array in constructor
Passed true in open variable.
Below is the working code for snack bar.
import React, { Component } from "react";
import Snackbar from "#material-ui/core/Snackbar";
class SnackBarSof extends Component {
constructor() {
super();
this.state = { orders: [1, 2], open: true };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
render() {
console.log(this.state.orders);
console.log(this.state);
const { open } = this.state;
return this.state.orders ? (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
open={open}
onClose={this.handleClose}
message="order confirmed"
autoHideDuration={2000}
></Snackbar>
</div>
) : (
<p>Loading...</p>
);
}
}
export default SnackBarSof;

Opening Modal from different component

CardComponent:
export class Card extends Component<Prop, State> {
state = {
isCancelModalOpen: false,
};
marketService = new MarketService();
deleteMarket = () => {
this.marketService
.deleteMar()
.then((response) => {
})
.catch((error) => {
console.log(error);
});
};
handleModalToggle = () => {
this.setState(({ isCancelModalOpen }) => ({
isCancelModalOpen: !isCancelModalOpen,
}));
};
render() {
const {isCancelModalOpen, isDropdownOpen } = this.state;
const dropdownItems = [
<DropdownItem
key="action"
component="button"
onClick={this.handleModalToggle}
>
Delete
</DropdownItem>
];
return (
{this.CancelModal.map((listing) => (
<DeleteModal handleModal={this.handleModalToggle}
deleteProd = {this.deleteProduct}
description = {listing.description} ></DeleteModal>
))}
);
}
}
DeleteModal Component:
interface Prop {
description: string;
handleModal: Function;
deleteProd: Function;
}
class DeleteModal extends Component<Prop, State> {
state = {
deleteConfirmModel: false
};
render() {
const { deleteConfirmModel } = this.state;
const {description} = this.props;
return (
<Modal
isSmall
title="Confirmation"
isOpen={deleteConfirmModel}
onClose={() => this.props.handleModal}
showClose = {false}
actions={[
<Button
key="confirm"
variant="primary"
onClick={() => this.props.deleteProd}
>
Delete
</Button>,
<Button key="cancel" variant="link" onClick={() => this.props.handleModal}>
Cancel
</Button>
]}
>
{description}
</Modal>
);
}
}
export default DeleteModal;
I want DeleteModal component to get open on click of Dropdown Delete button available in Card component. There is no error in the code, still I am not able to trigger modal on click of Dropdown from Card component. Can anyone help me with what's wrong with the code?
So I found the solution to call the modal from different component:
In the card component: to call deleteModal component:
<DeleteModal
displayModal={deleteModalOpen}
handleModal={this.handleModalToggle}
description="Are you sure you want to delete"
/>
DisplayModal, handleModal,decription will be props in DeleteModal component:
DeleteModal:
interface Prop {
displayModal: boolean;
handleModal: Function;
description: string;
}
<Modal
isSmall
title="Confirmation"
isOpen={this.props.displayModal}
onClose={() => this.props.handleModal()}
showClose={false}
actions={[
<Button
key="cancel"
variant="link"
onClick={() => this.props.handleModal()}
>
Cancel
</Button>,
]}
>
{this.props.description}
</Modal>

How to update image src when opening modal

I have this class component and I'm getting the data from a JSON file by using GraphQL. Everything works as expected but I find hard to update the image src inside of the Modal component when it's open. Data doesn't seems to get passed to the Modal and it shows the same image for all the cards. If I try using props it returns undefined in the image.src.
Any ideas or help on how to solve this would be great!!
my code:
import React from "react"
import { StaticQuery, graphql } from 'gatsby'
import { Container, Row, Col } from 'react-grid-system'
import Modal from 'react-responsive-modal'
class ProjectsList extends React.Component {
constructor(props) {
super(props)
this.state = {
open: false,
}
}
onOpenModal = () => {
this.setState({ open: true, modalImage: this.props });
};
onCloseModal = () => {
this.setState({ open: false });
};
render() {
const projects = this.props;
const { open } = this.state;
return(
<div>
<Row>
{projects.projects.map(item =>(
<Col md={6} key={item.id}>
<div className="project-card">
<div className="project-img-wrap">
<img src={item.image.src.publicURL} alt="projects" onClick={this.onOpenModal} />
</div>
<div className="project-text-wrap">
<span className="project-title">{item.title}</span>
</div>
</div>
<Modal open={open} onClose={this.onCloseModal} center>
<img style={{maxWidth: '800px'}} src={item.image.src.publicURL} alt="projects" />
</Modal>
</Col>
))}
</Row>
</div>
);
}
}
export default props => (
<StaticQuery
query={graphql`
query {
dataJson {
projects {
id
title
image {
src {
publicURL
}
}
}
}
}
`}
render={({ dataJson }) => <ProjectsList projects={dataJson.projects} {...props} />}
/>
)
I've made little edits to your code. That should work out.
The problem is that you haven't passed modalImage from your state to src in Modal image.
import React from "react"
import { StaticQuery, graphql } from 'gatsby'
import { Container, Row, Col } from 'react-grid-system'
import Modal from 'react-responsive-modal'
class ProjectsList extends React.Component {
constructor(props) {
super(props)
this.state = {
open: false,
modalImage: ""
}
}
onOpenModal = (image) => {
this.setState({ open: true, modalImage: image });
};
onCloseModal = () => {
this.setState({ open: false });
};
render() {
const projects = this.props;
const { open, modalImage } = this.state;
return(
<div>
<Row>
{projects.projects.map(item =>(
<Col md={6} key={item.id}>
<div className="project-card">
<div className="project-img-wrap">
<img src={item.image.src.publicURL} alt="projects" onClick={() => this.onOpenModal(item.image.src.publicURL)} />
</div>
<div className="project-text-wrap">
<span className="project-title">{item.title}</span>
</div>
</div>
<Modal open={open} onClose={() => this.onCloseModal()} center>
<img style={{maxWidth: '800px'}} src={modalImage} alt="projects" />
</Modal>
</Col>
))}
</Row>
</div>
);
}
}
export default props => (
<StaticQuery
query={graphql`
query {
dataJson {
projects {
id
title
image {
src {
publicURL
}
}
}
}
}
`}
render={({ dataJson }) => <ProjectsList projects={dataJson.projects} {...props} />}
/>
)

Open modal from another component click in react js

I am using the basic component modal component of react - https://github.com/reactjs/react-modal
What I am trying to achieve is that I want to open the modal from another parent that has the modal imported.
Parent.js
<button onClick={() => this.refs.setState({modalIsOpen: true})}> - THIS BUTTON ELEMENT IS IN ANOTHER COMPONENT
Modal.js
import React from 'react';
import ReactDOM from 'react-dom';
import Modal from 'react-modal';
const customStyles = {
content : {
top : '50%',
left : '50%',
right : 'auto',
bottom : 'auto',
marginRight : '-50%',
transform : 'translate(-50%, -50%)'
}
};
class App extends React.Component {
constructor() {
super();
this.state = {
modalIsOpen: false
};
this.openModal = this.openModal.bind(this);
this.afterOpenModal = this.afterOpenModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal() {
this.setState({modalIsOpen: true});
}
afterOpenModal() {
// references are now sync'd and can be accessed.
this.subtitle.style.color = '#f00';
}
closeModal() {
this.setState({modalIsOpen: false});
}
render() {
return (
<div>
<button onClick={this.openModal}>Open Modal</button>
<Modal
isOpen={this.state.modalIsOpen}
onAfterOpen={this.afterOpenModal}
onRequestClose={this.closeModal}
style={customStyles}
contentLabel="Example Modal"
>
<h2 ref={subtitle => this.subtitle = subtitle}>Hello</h2>
<button onClick={this.closeModal}>close</button>
<div>I am a modal</div>
<form>
<input />
<button>tab navigation</button>
<button>stays</button>
<button>inside</button>
<button>the modal</button>
</form>
</Modal>
</div>
);
}
}
export default App
I have read that this can be done using refs and changing the state of the modal. What exactly am I doing wrong here?
Thanks!
Can you try below code in parent
<button onClick={() => this._modal.openModal()}>click</button>
when you call your modal component use ref attribute then can call like above code.
<Modal ref={(modal) => { this._modal = modal; }} />
easy way, do this via props:
modal.js
import ....
<Modal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
className={classes.modal}
open={this.props.handleOpen}
onClose={this.props.handleClose}
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 1000
}}
>
in your component that has the modal imported.
///some code here
state = {
isOpen: Boolean(false)
};
<externalElement onClick={() => this.setState({ isOpen: true })}>title ... </externalElement>
<importedModal
handleOpen={this.state.isOpen}
handleClose={() => this.setState({ isOpen: false })}
/>

Categories

Resources