I'm new to React and I was trying to create a Modal using mui that opens and closes based on parent's state.
The problem is that the modal opens well based on state but upon closing, the modal onClose function works well, but when I click on Button that has the same with the same update state function it doesn't work.
Here's my parent component
const TableIcon = props => {
const {title,icon}=props;
// handling modal functionality
const [modalOpen, setModalOpen] = useState(false);
const handleModalOpen = () => {
setModalOpen(true);
};
const handleModalClose = () => {
setModalOpen(false);
};
console.log('modal', modalOpen)
return (
<button className={styles.button} title={title} onClick={()=>handleModalOpen()}>
{icon}
<ActionModal
open={modalOpen}
handleClose={handleModalClose}
handleOpen={handleModalOpen}
/>
</button>
)
}
and here is the modal component
const ActionModal=(props)=> {
const {open,handleOpen,handleClose}=props;
console.log(props,'modal')
return (
<div>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box className={styles.box}>
<div className={styles.header}>
<div className={styles.icon}>
<BsFillTrashFill/>
</div>
</div>
<div className={styles.content}>
<h2>You are about to delete a school</h2>
<h3>School Name/Tuituion</h3>
<p>This will delete the school from the database
Are you sure?</p>
</div>
<div className={styles.footer}>
<Button color='var(--unnamed-color-ffffff)' name='Cancel' onClick={handleClose}/>
<Button color='var(--unnamed-color-f53748)' name='Confirm'/>
</div>
</Box>
</Modal>
</div>
);
}
and here is the button component
const Button=(props)=>{
const {color,onClick,icon,name}=props;
return <div className={styles.container} style={{background:color}} onClick={()=>onClick()}>
{icon}
<span>{name}</span>
</div>
}
and the Button onClick works just fine
The problem is that your <ActionModal> component is inside the <button> tag. So when clicking the Cancel button in the modal you first get the call to handleModalClose, immediately followed by the call to handleModalOpen, because the "click" is getting passed to the button that opens the modal.
You need to change the code in <TableIcon> to something like:
return (
<>
<button title={title} onClick={() => handleModalOpen()}>
{icon}
</button>
<ActionModal open={modalOpen} handleClose={handleModalClose} />
</>
);
You can use this Stackblitz example.
Related
How to reload/refresh a parent component on button click.
In the code below i refresh page with javascript vanilla (window.location.reload(false)
Is there a way to reload the parent component or page without refreshing the page?
return product ? (
<div>
<div>
<img src={product.images[0]} alt={product.title} />
</div>
<div>
{product.title}
<br />
${product.price}
<br />
{product.description}
</div>
<div>
<button onClick={
() => {
setLiked(!liked)
if (favProduct) {
window.location.reload(false)
}
}
}>
<Icon size="28px" />
</button>
</div>
</div>
) : <p>Loading Product... </p>;
Not sure why you would want to do something like that, but here is one way to do it:
const Parent = () => {
const [toggleRefresh, setToggleRefresh] = useState(false)
return (
<Child setToggleRefresh={setToggleRefresh}/>
)
}
const Child = (props) => {
return (
<button onClick={
() => {
setLiked(!liked)
if (favProduct) {
//this will toggle the state of the parent, forcing a parent rerender
props.setToggleRefresh(prev => !prev)
}
}
}>
)
}
I have a homepage where they can click the modal button. after the button click the modal will show. then in the modal they will input a text and when they click submit the text they input should be display in the homepage. Currently the one i did is not displaying in homepage when click the submit button. Please help. Thank you
This is the code:
https://codesandbox.io/s/competent-rgb-lk4ws
Modal
In your Modal component you can create a event when the Submit button is clicked:
import React from "react";
import "./modal.css";
// Create new event: onSubmitClick
function Modal({ setOpenModal, onSubmitClick }) {
// It`s function get name value and pass to onSubmitClick
function getData() {
const nameValue = document.getElementById("name").value;
onSubmitClick(nameValue);
setOpenModal(false);
}
return (
<div className="modalBackground">
<div className="modalContainer">
<div className="titleCloseBtn">
<button
onClick={() => {
setOpenModal(false);
}}
>
X
</button>
</div>
<div className="title">Forms</div>
<div className="body">
<input id="name" placeholder="Enter Your Name" />
</div>
<div className="footer">
<button
onClick={() => {
setOpenModal(false);
}}
id="cancelBtn"
>
Cancel
</button>
{/* The onclick should be onClick */}
<button onClick={getData}>Submit</button>
</div>
</div>
</div>
);
}
export default Modal;
Home
In home page you can get the event and use the new name value
import React, { useState } from "react";
import Modal from "../../src/components/Modal/modal.js";
const Home = () => {
const [modalOpen, setModalOpen] = useState(false);
const [name, setName] = useState("");
function handleModalSubmitClick(nameValue) {
// Receive new value, and set in a state
setName(nameValue);
}
return (
<div class="hello">
<h1>Hey, click on the button to open the modal.</h1>
<span>name: {name}</span>
<button
onClick={() => {
setModalOpen(true);
}}
class="button is-pulled-right"
>
Button
</button>
{modalOpen && (
<Modal
setOpenModal={setModalOpen}
onSubmitClick={handleModalSubmitClick} // Bind a function to handle a event
/>
)}
</div>
);
};
export default Home;
I am developing an ecommerce application and I am working on the login.
I want the login to be a pop up box such that when the user click on login button the dialog box will appear and when the user click on the x button the dialog box disappears.
Other things work well except the close(x) button.
When it is clicked it refreshes the page instead of setting setOpenLoginModal to false.
I created a reusable modal then imported it to LoginModal.js which is passed to App.js.
Modal.js
const Modal = ({open, children, handleSubmit}) => {
if(!open) return null
return ReactDom.createPortal(
<>
<form action="" onSubmit={handleSubmit}>
<div style={OVERLAY_STYLE}>
<div style={MODAL_STYLES}>
{children}
</div>
</div>
</form>
</>,
document.getElementById('portal')
)
}
export default Modal
LoginModal.js
import Modal from './Modal'
const LoginModal = ({openLoginModal, setOpenLoginModal}) => {
return (
<div>
<Modal open={openLoginModal} handleSubmit={handleSubmit}>
<div className="d-flex justify-content-center">
<div>
<h4 className="head">eTranzact eCommerce</h4>
<h6 className="sub-heading">Create an account to list your own product</h6>
</div>
<div>
<h2 style={{position: 'absolute', right: '2em'}}>
<button className="close" type="button" onClick={() => setOpenLoginModal(false)}>x</button>
</h2>
</div>
</div>
<hr />
</div>
)
}
export default LoginModal
App.js
import LoginModal from '../Components/LoginModal';
const App = () => {
const [openLoginModal, setOpenLoginModal] = useState(false)
return (
<div>
{
openLoginModal && (
<LoginModal openLoginModal={openLoginModal} setOpenRegisterModal={ () => setOpenRegisterModal(false) } />
)
}
<div className="right-navs">
<button onClick={() => setOpenLoginModal(true)} className="btn btn-primary">
LOGIN
</button>
</div>
</div>
)
}
export default App
This issue is probably with your form, I don't think you are preventing the default. Even though you are saying action="" it will still try and process it like a get action which would be causing it to refresh the page.
const Modal = ({ open, children, handleSubmit }) => {
if (!open) return null
const submit = (e) => {
e.preventDefault()
handleSubmit()
}
return ReactDom.createPortal(
<>
<form action="" onSubmit={submit}>
<div style={OVERLAY_STYLE}>
<div style={MODAL_STYLES}>{children}</div>
</div>
</form>
</>,
document.getElementById('portal')
)
}
export default Modal
I can see you're passing the wrong setState handler to the child component. You have to change setOpenRegisterModal to setOpenLoginModal in your App.js file
I have a ref:
const ref = React.createRef();
I pass it like so:
<WinnerPopup winner={turn ? 'cross' : 'circle'} ref={ref}></WinnerPopup>
This is the component:
const WinnerPopup = React.forwardRef((props, ref) => (
<Popup
trigger={<button ref={ref} className="button"></button>}
modal
nested
>
{close => (
<div className="modal">
<button className="close" onClick={close}>
×
</button>
<div className="header"> We have a winner! </div>
<div className="content">
{`Winner is ${props.winner}`}
</div>
</div>
)}
</Popup>
));
And I get the error:
TypeError: Cannot read property 'click' of null
This is such a simple use case and almost exactly the same thing as the document so I cannot figure out why I can't access the button.
I think you normally use forwardRef when you want to pass along the ref to the outer most component of what you are wrapping with forwardRef... ie. you create a that is a wrapper around so you forward the ref onto the ... in this case you are passing it to the button you are passing into a prop (trigger) of your outer most component... maybe you want to just pass the reference in a custom prop, like "triggerButtonRef"... also, what purpose do you need the ref on that button?
You might try and pass the ref as a custom prop, instead of trying to use forwardRef, like:
<WinnerPopup winner={turn ? 'cross' : 'circle'} triggerRef={ref} />
const WinnerPopup = ({ triggerRef, winner }) => (
<Popup
trigger={<button ref={triggerRef} className="button"></button>}
modal
nested
>
{close => (
<div className="modal">
<button className="close" onClick={close}>
×
</button>
<div className="header"> We have a winner! </div>
<div className="content">
{`Winner is ${winner}`}
</div>
</div>
)}
</Popup>
);
Depending on how <WinnerPopup /> works and/or if that is also your own custom component, AND you want to be able to control if it's open from the parent or another component, I think a better solution might be to store a state variable to control whether or not the popup is open, for instance:
const ParentComponent = () => {
const [open, setOpen] = useState(false);
const onClick = () => {
setOpen(!open)
}
return (
<>
<SomeOtherButton onClick={onClick} />
<WinnerPopup winner={turn ? 'cross' : 'circle'} isOpen={open} />
</>
)
}
const WinnerPopup = ({ isOpen, winner }) => (
<Popup
trigger={<button className="button"></button>}
modal
nested
open={isOpen}
>
{close => (
<div className="modal">
<button className="close" onClick={close}>
×
</button>
<div className="header"> We have a winner! </div>
<div className="content">
{`Winner is ${winner}`}
</div>
</div>
)}
</Popup>
);
I have a click event to show a popup window in the parent and need to pass it down to List child and then to Item child and then to List child. When user clicks the Link component, it opens the pop up. It does not work right now, "TypeError: onCourseSelect is not a function. My code is below. Please help!
App.js
onCourseSelect=(course)=>{
this.setState({selectedCourse:course
,showPopup:true
});
};
<List course={course} onCourseSelect={this.onCourseSelect} />
List.js
const List=(course,onCourseSelect)=>{
return(
<Item key={course.ID} course={course} onCourseSelect={onCourseSelect} />)}
)}
Item.js
const Item=({course, onCourseSelect})=>{
return(<Link onCourseSelect={onCourseSelect(course)} course={course}></Link> )
}
Link.js
const Link=({course, onCourseSelect})=>{
return (
<div>
<input type="button" value="View More" onClick={onCourseSelect(course)} />
</div>
);
}
In Item.js you wrote onCourseSelect={onCourseSelect(course)} which should be rather onCourseSelect={onCourseSelect} as you just have to pass this as a prop to child component where it can be called. The course is passed as a separate prop in component.
On <input> you can then do, onClick = {()=>{ onCourseSelect(course) }}.
Writing onClick = { onCourseSelect(course) } will call the method onCourseSelect on each re-render.
App.js
onCourseSelect=(course)=>{
this.setState({
selectedCourse:course,
showPopup:true
});
};
<List course={course} onCourseSelect={this.onCourseSelect} />
List.js
const List=({course,onCourseSelect})=>{
return(
<Item key={course.ID} course={course} onCourseSelect={onCourseSelect} />)}
)}
Item.js
const Item=({course, onCourseSelect})=>{
return(<Link onCourseSelect={onCourseSelect} course={course}></Link> )
}
Link.js
const Link=({course, onCourseSelect})=>{
return (
<div>
<input type="button" value="View More" onClick={()=> onCourseSelect(course)} />
</div>
);
}