I'm using a Dialog component from Material-UI which, for the most part, is working correctly. However, if I click away from the Dialog, the Dialog will disappear (as expected) but sometimes, it remains in the DOM with its opacity set to 0, and I can't click anything else since the Dialog is in the way. This is a small sample of my code:
const [openDialog, setOpenDialog] = React.useState(false);
React.useEffect(() => {
// Get data for ReactTable
}, []);
return(
<div>
// Other components
<Button color="white" onClick={() => setOpenDialog(true)}>
Open Dialog
</Button>
// Other components
<Dialog open={openDialog} maxWidth="md" onClose={() => setOpenDialog(false)}>
// ReactTable and close button
</Dialog>
</div>
)
This bug doesn't always occur which makes it tricky to debug. I've only been using React for about a month, but I'm wondering if it's a state problem, or maybe some sort of race condition. Any suggestions?
Edit: This also occurs when a DropzoneDialog appears, to upload a file.
This also works:
<Dialog className={openDialog ? "" : classes.displayNone} open={openDialog} maxWidth="md" onClose={() => setOpenDialog(false)}>
// ReactTable and close button
</Dialog>
where in the styles file you have:
displayNone: {
display: "none"
}
In case anyone else has this same issue, I found the answer:
Elsewhere in the app, useEffect() was stuck in a loop and running extremely frequently which slowed the app down, causing this problem.
Related
I am trying to change my URL after a modal is clicked.
I had added an extra onClick to the button which called the modal, this was to a function - in that function I added some console logging. I could see the logging, but the URL didn't change.
The (original) button code is:
<button className='buttonCheck' onClick={checkAnswer}>CHECK MY ANSWER</button> <CheckAnswerModal showModal={showCongratsModal} onClose={() => setShowCongratsModal(false)} videoMessage={showCongratsURL} content={showWindowContent} size='med'/>
And the modal
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
function CheckAnswerModal({showModal = false, onClose = () =>{}, videoMessage, content, size}) {
return (
<Modal
size={size}
show={showModal}
onHide={onClose}
backdrop="static"
keyboard={false}
>
<Modal.Body>
{videoMessage ? <video src={videoMessage} controls autoPlay> </video>: <div><center>{content}</center></div>}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={onClose}>
Close
</Button>
</Modal.Footer>
</Modal>
)
}
export default CheckAnswerModal
Originally, I had added a new function to the calling page:
function GoHome() {
console.log("redirecting")
<Redirect to='/' />
}
and added this to the button onClick, I could see the logging, but no URL changing. I've done a bit of looking about and I am pretty sure this is not working as the modal is on screen. To get around this, in the GoHome() I even added a conditional (if the showModal is false, then do the logging)
I've seen some posts which talked about unmounting the component (now - this is something new to me, especially as I don't call any mount component explicitly.)
Am I missing something fundamental with redirect? Or can someone point me at what I am doing wrong (the redirect feels a little "hacky" just now, I need to redo a whole component I think, but this would work for now)
You have to install react-router-dom if you have not installed yet.
Then import it.
import {useNavigate} from react-router-dom
then call it inside a function like:
const navigate = useNavigate()
then in your onClose() function use navigate like:
navigate("/")
sorry for my English
Rather than trying to be smart, I simply declared history at the start and then in my onClose added history.push and it worked.
Im using react-native-modal
This was my modal
import Modal from 'react-native-modal';
..
....
..
<Modal
visible={modalVisible} //this one was the issue (should be isVisible)
animationType={'slide'}
swipeDirection="down"
transparent={true}
onRequestClose={() => closeModal()}
onSwipeComplete={() => closeModal()}
style={{justifyContent: 'flex-end', margin: 0}}
>
</Modal>
So as you can see, my idea was to open it half way and slide it down to close
My problem was that it was acting weird, the modal wasn't updating properly, sometimes the modal opened way less than it was supposed to
After reading the documentation and common problems:
The solution was to change the visible prop to isVisible
isVisible={modalVisible}
So I have a React job interview and have this piece of code in my assignment to close and modal box:
const App = (): ReactElement => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const renderApp = (): ReactElement => {
if (isOpen) {
return <FeedbackForm onClose={() => setIsOpen(false)} />;
}
return <button onClick={() => setIsOpen(true)}>Open modal</button>;
}
if (isOpen) {
return <>
<div className='App'>
<FeedbackForm onClose={() => setIsOpen(false)} />
</div>
</>
}
return <>
<div className='App'>{renderApp()}</div>
}
export default Feedback;
So here I have an App component that has a Modal component and a simple hook that has a boolean value that indicates if the modal is open or not. The modal starts off as closed, so the initial value is false. The button to open the modal is separate from the button that has to close the modal. The button to close the modal is within the Modal component while the button to open it is in the App component. Because they're both separate buttons, they can explicitly set the value to either true or false.
Now, on this topic, I received this question from the code reviewer:
Could you explain the potential issue of the following code?
const [isOpen, setIsOpen] = useState(false)
const toggle = () => setIsOpen(!isOpen);
I can't exactly see the problem in this snippet. I've tried running it with a button that calls toggle() and I see it updating the state perfectly fine. What is the potential issue the interviewer is talking about here?
In the toggle function, we are setting the state based on the current state value. React performs updates in batches and it is a possibility that when we run toggle() multiple times, the state is not updated for each subsequent run. So, to make sure that we don't lose the state updates from the previous runs, we can do something like the following:
const toggle = () => setIsOpen((prevIsOpen) => !prevIsOpen);
Now, in your scenario, it might not happen because we are showing/hiding modal based on toggle click which changes the views completely, but we can't be sure and can't take chances for production deployment. If it were something else, like showing/hiding of expansion panel, sidebar, etc. then the issue would be easily visible if you clicked toggle button simultaneously, with minimal delay between clicks.
I'm a little bit confused with the SwipeableDrawer explanation on the Material-ui website. Basically I have a Component 'Sidebar' which opens a SwipeableDrawer if a user clicks on button on the appbar, or a user swipes to open up the sidebar.
In the appbar there's a button you can press which gets passed to the parent component.
// Topbar.js
<IconButton color="inherit" onClick={onSidebarOpen}>
<MenuIcon/>
</IconButton>
// Main.js
<Topbar onSidebarOpen={this.handleSidebarOpen}/>
The handelSidebarOpen method sets a state of whether the sidebar is open or closed. So now the problem is that I'm not entirely sure how to tell the Sidebar properly to open or close the drawer if a user swipes the drawer open.
I used this approach
<Sidebar
open={this.state.openSidebar}
onClose={this.handleSidebarClose}
/>
And then in the Sidebar class I do this
// Inside render method
const {open, onClose} = this.props;
return (
<SwipeableDrawer
open={open}
onOpen={event => this.showDrawer(event)}
onClose={onClose}
disableBackdropTransition={!iOS}
disableDiscovery={iOS}
>
{console.log(onClose)}
{this.fullList()}
</SwipeableDrawer>
);
Please feel free to ask me for clarification if you don't understand the problem. I've made a little demo to show the problem.
https://codesandbox.io/embed/dazzling-galileo-mc3oz?fontsize=14&hidenavigation=1&theme=dark
Try to swipe the sidebar open and watch what happens. Thanks in advance.
Just pass handleSidebarOpen method in Sidebar at your Main.js file.
<Sidebar
open={this.state.openSidebar}
onOpen={this.handleSidebarOpen}
onClose={this.handleSidebarClose}
/>
Get that function in your Sidebar.js and use it on onOpen attribute of SwipeableDrawer. Like below,
const { open, onOpen, onClose } = this.props;
return (
<SwipeableDrawer
open={open}
onOpen={onOpen}
onClose={onClose}
disableBackdropTransition={!iOS}
disableDiscovery={iOS}
>
{console.log(onClose)}
{this.fullList()}
</SwipeableDrawer>
);
I have also created sandbox for you;
https://codesandbox.io/s/react-gki1u?fontsize=14&hidenavigation=1&theme=dark
I'm trying to createa modal component that can be "opened" with different props. Everything works fine (sending the props), with one exception.
I cant open the model from the parent....If i use the Child (modal) button it works fine, if i try to use a parent button i can't do anything.
The modal is this one -> https://material-ui.com/utils/modal/
I've tried sending the "open" prop from parent to child, but that messes everything up (as in i see like a lot of modals opened and cant close them).
Here's how it looks like.
Modal (Child) component ->
<Modal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
open={this.props.open}
onClose={this.handleClose}
>
And from my Parent component (posts on a page) i've tried this ->
<Modal modalTitle={post.acf.title} open={this.state.modalOpen}/>
I have defined the state in my parent as modalopen: false, but i always get an error on the modal saying that the prop is undefined UNLESS i also add
PortModal.defaultProps = {
open: false,
}
If i check in React DevTools i can see that my modals were created with open={false} and also they are sending the modal title properly (which works).
I don't understand why the modals open like 1000 times and i can't close them.
Is there any way to make this work with props or is there something else that i should try? I've tried using refs, but i am not really sure i did it right.
Thanks in advance.
Update with more code & a gif.
How i'm currently creating it is pretty much the same, but i also pass the close handler.
In componentDidMount
const MyModal = <Modal modalTitle={this.state.title} open={this.state.modalOpen} openHandler={this.handleOpen}/>
this.setState({
MyModal
});
In render() i simply return that const.
const { MyModal } = this.state;
<div>
{MyModal}
</div>
And my modal looks like this ->
<div>
<Typography gutterBottom>Click to get the full Modal experience!</Typography>
<Button onClick={this.props.openHandler}>Open Modal</Button>
<Modal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
open={this.props.open}
onClose={this.props.closeHandler}
>
<div style={getModalStyle()} className={classes.paper}>
<Typography variant="title" id="modal-title">
Text in a modal
</Typography>
<Typography variant="subheading" id="simple-modal-description">
Duis mollis, est non commodo luctus, nisi erat porttitor ligula.
</Typography>
<PortModalWrapped />
</div>
</Modal>
</div>
So its pretty much the default card from the link above.
I tried calling it by either clicking the modal button, or another button that i have in my parent, which looks like this ->
<Button size="small" color="primary" onClick={openModal(post.acf.title)}>
And the function is
const openModal = (title) => {
this.setState({
title: title,
openModal: true,
});
};
None of the buttons work (i know i dont send the closehandler, its no use to even test that since i cant open the modal).
It does have a weird glitch whenever i click any of the buttons (scrollbar appearing and disappearing rly fast like it would be changing state or smthn).