In react.js, I want setShowModal to be false when the Backdrop component is clicked and the Backdrop component to be hidden, but setShowModal is not false and does not even show console.log ('a').
import { useState } from 'react';
import Backdrop from './Backdrop';
import Modal from './Modal';
function Todo(props) {
const [showModal, setShowModal] = useState(false);
function showModalHandler() {
setShowModal(true);
}
function closeModalHandler() {
setShowModal(false);
console.log('a');
}
return (
<div className='card'>
<h2>{props.text}</h2>
<div className='actions'>
<button className='btn' onClick={showModalHandler}>
Delete
</button>
</div>
{showModal && <Modal />}
{showModal && <Backdrop onClick={closeModalHandler} />}
</div>
);
}
export default Todo;
it seems that there was a problem with the binding of the closeModalHandler function. I've made some changes to your code and it is actually closing the modal and dropping the 'a' in the console.
Defining the functions as arrow functions use to help in these cases.
Hope it works to you.
import { useState } from 'react';
function Todo(props) {
const [showModal, setShowModal] = useState(true);
const showModalHandler = () => {
setShowModal(true);
}
const closeModalHandler = () => {
setShowModal(false);
console.log('a');
}
return (
<div className='card'>
<h2>{props.text}</h2>
<div className='actions'>
<button className='btn' onClick={() => showModalHandler()}>
Delete
</button>
</div>
{showModal && <button> ShowModalIsActive</button>}
{showModal && <button onClick={() => closeModalHandler()}>Button</button>}
</div>
);
}
export default Todo;
Related
I am having two components, App and a panel. On button clcik, I add panel to the screen and all the actions corresponding actions inside of the panel is handled in the Panel component ( Actions are expand, collapse and close). Can I somehow execute the same actions inside of the app component using useImperativeHandle hook using ref's. Also can I execute onClose method inside of the Panel component, here i am actually as a callback.
https://codesandbox.io/s/basic-demo-card-6ywop7?file=/src/Panel.jsx:0-985
Can someone help me here
App
import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";
import Panel from "./Panel";
import "./styles.css";
function App() {
const [card, setCard] = useState({
cardId: "",
cardBody: null
});
const ref = useRef();
const handleClick = (cardId, cardBody) => {
setCard({ cardId, cardBody });
};
const { cardId, cardBody } = card;
return (
<>
<div className="main">
<button onClick={() => ref?.current?.expandBtn()}>Open from out</button>
<button onClick={() => handleClick("Panel 1", <h1>h1</h1>)}>
Add Panel 1
</button>
<button onClick={() => handleClick("Panel 2", <div>div</div>)}>
Add Panel 2
</button>
</div>
{cardBody && (
<div className="cards-container">
<Panel
key={cardId}
cardId={cardId}
cardBody={cardBody}
onClose={() =>
setCard({
cardId: "",
cardBody: null
})
}
/>
</div>
)}
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Panel
import React, { useImperativeHandle, useState, useRef } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {
faSquareMinus,
faRectangleXmark
} from "#fortawesome/free-solid-svg-icons";
export default function Panel(props) {
const [isMinimized, setIsMinimized] = useState(false);
const { cardId, cardBody, onClose, ref } = props;
const expandRef = useRef();
useImperativeHandle(ref, () => {
return {
expandBtn: () => expandRef.current.onMaximize()
};
});
const onMaximize = () => {
setIsMinimized(!isMinimized);
};
return (
<>
<div className={isMinimized ? "card-min" : "card"}>
<div className="card-actions">
<span onClick={onMaximize}>{cardId}</span>
{!isMinimized && (
<FontAwesomeIcon
icon={faSquareMinus}
onClick={() => {
setIsMinimized(true);
}}
/>
)}
<FontAwesomeIcon icon={faRectangleXmark} onClick={onClose} />
</div>
<div className="card-body">{cardBody}</div>
</div>
</>
);
}
Yes, you can the child's function from the Parent using useImperativeHandle. What your implementation is missing is forwardRef. passing ref as props won't work. What you have to do is forward the ref from child to parent.
const Panel = React.forwardRef(function (props, ref) {
const [isMinimized, setIsMinimized] = useState(false);
const { cardId, cardBody, onClose } = props;
const onMaximize = () => {
setIsMinimized(!isMinimized);
};
useImperativeHandle(ref, () => {
return {
expandBtn: onMaximize
};
});
return (
<>
<div className={isMinimized ? "card-min" : "card"}>
<div className="card-actions">
<span onClick={onMaximize}>{cardId}</span>
{!isMinimized && (
<FontAwesomeIcon
icon={faSquareMinus}
onClick={() => {
setIsMinimized(true);
}}
/>
)}
<FontAwesomeIcon icon={faRectangleXmark} onClick={onClose} />
</div>
<div className="card-body">{cardBody}</div>
</div>
</>
);
})
export default Panel;
Pass the ref from the App
function App() {
const [card, setCard] = useState({
cardId: "",
cardBody: null
});
const ref = useRef();
const handleClick = (cardId, cardBody) => {
setCard({ cardId, cardBody });
};
const { cardId, cardBody } = card;
return (
<>
<div className="main">
<button onClick={() => ref?.current?.expandBtn()}>Open from
out</button>
<button onClick={() => handleClick("Panel 1", <h1>h1</h1>)}>
Add Panel 1
</button>
<button onClick={() => handleClick("Panel 2", <div>div</div>)}>
Add Panel 2
</button>
</div>
{cardBody && (
<div className="cards-container">
<Panel
ref={ref}
key={cardId}
cardId={cardId}
cardBody={cardBody}
onClose={() =>
setCard({
cardId: "",
cardBody: null
})
}
/>
</div>
)}
</>
);
}
This might help https://blogsbyarjun.hashnode.dev/how-to-update-childs-state-from-parent-in-react-1
I have this Alert component to be used just to have a message that says "Successfully submitted" and I'm trying to use thin in a parent component. However, nothing shows in the parent component.
AlertComponent
import React, { useState } from "react";
import { Snackbar, Alert } from "#mui/material";
const AlertComponent = () => {
const [open, setOpen] = useState(false);
const handleClick = () => {
setOpen(true);
};
const handleClose = (event, reason) => {
if (reason === "clickaway") {
return;
}
setOpen(false);
};
return (
<div>
{" "}
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
<Alert onClose={handleClose} severity="success">
Successfully Submitted!
</Alert>
</Snackbar>
</div>
);
};
export default AlertComponent;
Parent Component
const ParentComponent = () => {
const [open, setOpen] = useState(false);
const onSubmit = async (data) => {
//codes to submit the data
setOpen(true); //trigger the alert component
};
return (
<div>
//form here to submit
<AlertComponent open={open} />
</div>
);
};
export default ParentComponent;
How can I fix this? Thank you.
Although #Ghader Salehi commented already the solution but if anyone is not sure how to control the alert from parent here is the code.
AlertComponent.js
import React from "react";
import { Snackbar, Alert } from "#mui/material";
function AlertComponenet(props) {
const { open, handleClose } = props;
return (
<div>
{" "}
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
<Alert onClose={handleClose} severity="success">
Successfully Submitted!
</Alert>
</Snackbar>
</div>
);
}
export default AlertComponenet;
Parent Component (In my code-sandbox I used App.js)
import React, { useState } from "react";
import "./styles.css";
import AlertComponent from "./AlertComponent";
export default function App() {
const [open, setOpen] = useState(false);
const handleClick = () => {
setOpen(true);
};
const handleClose = (event, reason) => {
if (reason === "clickaway") {
return;
}
setOpen(false);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={handleClick}>Show Alert</button>
<AlertComponent open={open} handleClose={handleClose} />
</div>
);
}
I have a register page and Modal component. In register has a useState for visibility of the Modal. I'm passing it as a prop to Modal. When the modal is closed how to change the useState value in the register page.
Register page:
import React, { useState } from 'react'
import {
CCard,
CButton,
CCardBody,
CCardHeader,
CCol,
CForm,
CFormInput,
CFormLabel,
CSpinner,
CRow,
} from '#coreui/react'
import CIcon from '#coreui/icons-react'
import { cilSend } from '#coreui/icons'
import Alert from 'src/components/Alert'
import Modal from 'src/components/Modal'
const FormControl = () => {
const [disabled, setDisabled] = useState(false)
const [visible, setVisible] = useState(false)
const [email, setEmail] = useState('')
const [name, setName] = useState('')
const handleAddMember = async () => {
try {
const data = { email, name }
const _data = await fetch('http://localhost:4000/api/v1/member/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + localStorage.getItem('token'),
},
body: JSON.stringify(data),
})
if (_data.status === 201) {
setVisible(true)
setDisabled(false)
} else if (_data.status === 422) {
setDisabled(false)
} else {
setDisabled(false)
throw new Error()
}
} catch (err) {
setDisabled(false)
}
}
return (
<CRow>
<Modal visible={visible} message="Member added to your community successfully!" />
<CCol xs={6}>
<CCard className="mb-4">
<CCardHeader>
<strong>Add New Member</strong>
</CCardHeader>
<CCardBody>
<p className="text-medium-emphasis small">
Fill in the email address field and name field to add a new member to your community.
</p>
<CForm>
<div className="mb-3">
<CFormLabel>Email address:</CFormLabel>
<CFormInput
type="email"
placeholder="name#example.com"
onChange={(e) => {
setEmail(e.target.value)
}}
/>
</div>
<div className="mb-3">
<CFormLabel>Name:</CFormLabel>
<CFormInput
type="text"
placeholder="Perera's Home"
onChange={(e) => {
setName(e.target.value)
}}
/>
</div>
<div className="mb-3">
<CButton color="primary" disabled={disabled} onClick={() => handleAddMember()}>
{disabled ? (
<CSpinner component="span" className="me-2" size="sm" aria-hidden="true" />
) : (
<CIcon icon={cilSend} className="me-2" />
)}
Submit
</CButton>
</div>
</CForm>
</CCardBody>
</CCard>
</CCol>
</CRow>
)
}
export default FormControl
Modal component:
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { CButton, CModal, CModalBody, CModalFooter, CModalHeader, CModalTitle } from '#coreui/react'
const Modal = (props) => {
const [visible, setVisible] = useState(props.visible)
return (
<CModal alignment="center" visible={visible} onClose={() => setVisible(false)}>
<CModalHeader>
<CModalTitle>Success!</CModalTitle>
</CModalHeader>
<CModalBody>{props.message}</CModalBody>
<CModalFooter>
<CButton color="primary" onClick={() => setVisible(false)}>
Close
</CButton>
</CModalFooter>
</CModal>
)
}
Modal.propTypes = {
visible: PropTypes.bool,
message: PropTypes.string,
}
export default React.memo(Modal)
You should have just one visible state member, either in the parent component or in the child (Modal), rather than having it in both places.
If you put it in the parent, you can pass it to the child just like any other prop:
return <Modal visible={visible} setVisible={setVisible}>{/*...*/}</Modal>
Modal's code can then call props.setVisible with the appropriate flag.
If you only want Modal to be able to hide itself (not show itself), you might instead pass a wrapper function that calls setVisible(false):
const hide = useCallback(() => setVisible(false), [setVisible]);
// Optional, see below −−−−−−−−−−−−−^^^^^^^^^^
// ...
return <Modal visible={visible} hide={hide}>{/*...*/}</Modal>
...and then Modal's code calls hide() to hide the modal.
(Making setVisible a dependency in the useCallback call is optional; state setter functions are stable; they don't change during the lifetime of the component. Some linters aren't quite smart enough to realize that and may nag you if you don't include it, but most are smarter than that.)
Here's a highly simplified example:
const {useState} = React;
const Example = () => {
const [visible, setVisible] = useState(false);
return <div>
<input type="button" value="Open" disabled={visible} onClick={() => setVisible(true)} />
<Modal visible={visible} setVisible={setVisible} />
</div>;
};
const Modal = (props) => {
if (!props.visible) {
return null;
}
return <div className="modal">
<div>This is the modal</div>
<input type="button" value="Close" onClick={() => props.setVisible(false)} />
</div>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
.modal {
border: 1px solid grey;
padding: 4px;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
Or with destructuring (I generally use destructuring with props, but it didn't look like you were):
const {useState} = React;
const Example = () => {
const [visible, setVisible] = useState(false);
return <div>
<input type="button" value="Open" disabled={visible} onClick={() => setVisible(true)} />
<Modal visible={visible} setVisible={setVisible} />
</div>;
};
const Modal = ({visible, setVisible}) => {
if (!visible) {
return null;
}
return <div className="modal">
<div>This is the modal</div>
<input type="button" value="Close" onClick={() => setVisible(false)} />
</div>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
.modal {
border: 1px solid grey;
padding: 4px;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
you can pass the setVisible as well in the modal component and then use the same setState on both component
<Modal visible={visible} setVisible={setVisible} message="Member added to your community successfully!" />
use this like
props.visible
props.setVisible
I am trying to create a modal that I can reuse/call from multiple components. I want the modal to display in app.js but the button call is on another component.
Once I am able to implement one, I can but button on other components and call same modal instead of having to create the same modal for each component
<div className="App">
<HeroSlider />
<HowItWorks />
<Modal />
<Footer />
</div>
The modal button is on this component(HeroSlider). Once its click it will call the modal component and display it in app.js
import React, { useState } from "react";
import Header from './Header'
function HeroSlider(props) {
const [show, setShow] = useState(false);
const modalShow = () => setShow(true);
const openIsAccount = () => {
}
return (
<div className="jumbotron" id="jumbotron2" >
<button type="button" className="btn btn-primary" id="shoutBtn" onClick={modalShow}><span>Get Started</span>
</button>
</div>
);
}
export default HeroSlider;
Here is the Modal.js
const IsAccountOpen = (props) => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<>
<Modal show={props.show} onHide={handleClose} backdrop="static" keyboard={false}>
<Modal.Header closeButton>
<Modal.Title>Modal title</Modal.Title>
</Modal.Header>
<Modal.Body>
I will not close
</Modal.Body>
</Modal>
</>
);
};
export default IsAccountOpen
You need to add a function in the app.js to handle the click on the button, so the function will change the state show to true, and you will pass the state to your modal, like this:
App.js
[showState, setShowState] = useState(false)
buttonClickedHandler = () => {
setShowState((showState) => showState = !showState )
}
<div className="App">
<HeroSlider buttonClicked={buttonClickedHandler} />
<HowItWorks />
<Modal show={showState} buttonClicked={buttonClickedHandler} />
<Footer />
</div>
HeroSlider.js
import React, { useState } from "react";
import Header from './Header'
function HeroSlider(props) {
const [show, setShow] = useState(false);
const modalShow = () => setShow(true);
const openIsAccount = () => {
}
return (
<div className="jumbotron" id="jumbotron2" >
<button type="button" className="btn btn-primary" id="shoutBtn" onClick={props.buttonClicked}><span>Get Started</span>
</button>
</div>
);
}
export default HeroSlider;
IsAccountOpen.js
const IsAccountOpen = (props) => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<>
<Modal show={props.show} onHide={props.buttonClicked} backdrop="static" keyboard={false}>
<Modal.Header closeButton>
<Modal.Title>Modal title</Modal.Title>
</Modal.Header>
<Modal.Body>
I will not close
</Modal.Body>
</Modal>
</>
);
};
export default IsAccountOpen
I think the best solution is to use redux, because you will need to access and update the state from different components, you shouldn't use context API because the state changes frequently and you will hit the performance.
You'll need to extract the state to App level, so that this can be shared to components. You can do this with useState, or using context & custom hooks if you want to make it very clean. But do it with useState on first.
HeroSlider should receive modalShow function in it's props.
Modal.js should receive the show state as props.
Here I have my modal component. I am making an app that I want a button to open this modal that I use in multiple places like opening a preview or deleting options.
import React from 'react';
import ReactDOM from 'react-dom';
import { CSSTransition } from 'react-transition-group';
import Backdrop from '../Backdrop/Backdrop';
import '../Modal/Modal.css';
const ModalOverlay = (props) => {
const content = (
<div className={`modal ${props.className}`} style={props.style}>
<header className={`modal__header ${props.headerClass}`}>
<h2>{props.header}</h2>
</header>
<form
onSubmit={
props.onSubmit ? props.onSubmit : (event) => event.preventDefault()
}
>
<div className={`modal__content ${props.contentClass}`}>
{props.children}
</div>
<footer className={`modal__footer ${props.footerClass}`}>
{props.footer}
</footer>
</form>
</div>
);
return ReactDOM.createPortal(content, document.getElementById('modal-hook'));
};
const Modal = (props) => {
return (
<React.Fragment>
{props.show && <Backdrop onClick={props.onCancel} />}
<CSSTransition
in={props.show}
mountOnEnter
unmountOnExit
timeout={200}
classNames="modal"
>
<ModalOverlay {...props} />
</CSSTransition>
</React.Fragment>
);
};
export default Modal;
And here I use this modal for showing up deleting options.
const DocumentItem = (props) => {
const [showConfirmModal, setShowConfirmModal] = useState(false);
const showDeleteWarningHandler = () => {
setShowConfirmModal(true);
};
const calcelDeleteHandler = () => {
setShowConfirmModal(false);
};
const confirmDeleteHandler = () => {
setShowConfirmModal(false);
console.log('Delete!');
};
return (
<React.Fragment>
<Modal
show={showConfirmModal}
onCancel={calcelDeleteHandler}
header="Are you sure?"
footerClass="document-item__modal-actions"
footer={
<React.Fragment>
<Button inverse onClick={calcelDeleteHandler}>
CANCEL
</Button>
<Button danger onClick={confirmDeleteHandler}>
DELETE
</Button>
</React.Fragment>
}
>
<p>
Do you want to proceed and delete this document? Please note that it
can't be undone thereafter.
</p>
</Modal>
</React.Fragment>
);
};
I don't understand why my screen goes all black, transparent but my modal doesn't show.
How can I fix this problem?