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).
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.
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.
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 have a modal page popping up when the user clicks a button, it's working perfectly :
render() {
return (
<div>
<section>
<button onClick={() => this.refs.simpleDialog.show()}>Open Modal</button>
</section>
<SkyLight hideOnOverlayClicked ref="simpleDialog" title="Test Modal">
Text that appears inside the modal page
<Button onClick={() => this.refs.simpleDialog.hide()} >Got It</Button>
</SkyLight>
</div>
)}
But My goal is to open the modal automatically when the user opens the page for the first time.
I don't want to open the modal page by clicking on a button
Question:
Can I use an IIFE (An immediately-invoked function expression) in order to open the modal as soon as the user open the page ?
My approach was to set a boolean to true. Then open the modal if the value is set to true
Library being used for the modal :
https://github.com/marcio/react-skylight
I think what you're looking for is the componentDidMount() lifecycle method:
componentDidMount() {
this.refs.simpleDialog.show();
}
From the React docs:
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
Feel free to checkout other component lifecycle methods.
To have a model open on component mount, just set isVisible to true
<SkyLight isVisible={true} ref="simpleDialog" title="Test Modal">
I am using ReactJS with Material-UI, which I am both quite new to using.
I have an AppBar component that is used as a main menu/navigation bar. I would like the title of the AppBar component to reflect the page I'm on. So if I press a button that routes the user to /login , I also want to change the title of the AppBar component to "Login". I have accomplished this but there seem to be some side effects so I doubt it's the proper practice.
At the moment, my AppBar component looks like so:
render() {
let MenuOptionsNotLogged = ({}) => (
<div>
<FlatButton
label="Log In"
containerElement={<Link to="/login" />}
secondary
linkButton
onClick={this.switchToLogin}
/>
</div>
);
return (
<div>
<MuiThemeProvider muiTheme={muiTheme}>
<div className="headerDiv">
<AppBar
className="appBar"
showMenuIconButton={this.state.appBarIcon}
title={this.state.appBarTitle}
iconElementRight={<MenuOptionsNotLogged />}
iconElementLeft={<IconButton><BackIcon /></IconButton>}
/>
</div>
</MuiThemeProvider>
</div>
);
In my constructor, I set the default state of the AppBar(the title and icon):
constructor(props, context) {
super(props, context);
this.switchToLogin = this.switchToLogin.bind(this);
this.state = {
appBarTitle: 'Home',
appBarIcon: false
};
}
And then I have a function which changes the title and icon when the user navigates to the Login page:
switchToLogin() {
this.setState({ appBarTitle: 'Log in' });
this.setState({ appBarIcon: true });
}
The problem
This 'works', but it has some problems if the user refreshes or hits the back button on their browser.
E.g. user navigates to /login, title changes to "Login", user refreshes browser. Then the user will be on /login but the title will reset to "Home".
Same problem with the back button. User can navigate to login, press back, and the title will stay as "Login" as opposed to "Home"
I see you have a Link component. My answer assumes you use react-router.
The problem is that your appBarTitle state is not mapped correctly with the current route.
Also when user refresh the browser, the state of components resets.
if you are not using reat-router 3.0, you can get access to the current route through this.context.location.pathname. You will need to read about how to use context here.
Then you can write a helper function to map pathname to the text you want to display: '/login' -> 'Login', '/' -> 'Home'.
Then the title of your AppBar can just be the value returned from the helper function. You no longer need to keep tract of it in the state.
Just to add, not relating to your question, Reactjs is a declarative style of programming, meaning that, 'switchToLogin then display login title' is discouraged. Tell the component what to render rather than how to.