How to create a Modal in react triggered without button - javascript

I want to create a model in react material UI library which shows the info on the landing page as the user logs in.
I have tried triggering with timeout but im unable to close the modal once it appears.
const[post,setPost] = useState('')
const [open, setOpen] = useState(false);
const handleClose = () => {
setOpen(false);
};
useEffect(() =>{
setTimeout(()=>{
setNotesPopup(true);
},3000);
},[]);
const Popup=((props) =>{
return(props.trigger)
})
return (
<>
<NavigationBar/>
<SydBackdrop openBackdrop={openBackdrop} />
<Container maxWidth="xl" className="mb-2">
<Card className={classes.card}>
<CardHeader title="Customer Search Parameters" className={classes.cardHeader}/>
<CardContent>
<Dialog trigger={notesPopup} setTrigger={setNotesPopup} aria-labelledby="customized-dialog-title" open={open}>
<DialogTitle id="customized-dialog-title"> UI changes</DialogTitle>
<DialogContent dividers><Typography gutterBottom><Markdown>{post}</Markdown></Typography></DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="primary">OK</Button>
</DialogActions>
</Dialog>
</>
)
}

Consider using a global state that is available to your entire app like Redux or React's context api.
Put a property that has modalStatus as false. When the user logs in, update that value in your global state to true and when the user closes the modal, it sets it to false

Related

Dialog of material UI has afterimage when being closed

Introduction
Bascially <Dialog /> receives open and onClose as props. open is boolean from state and onClose is a function that changes the state.
I made <CustomModal /> that wraps <Dialog />, which receives another prop content that defines what to display on <Dialog />.
// CustomModal.jsx
const CustomModal = props => (
<Dialog {...props} sx={{ '& .MuiDialog-paper': { padding: '2em' } }}>
{props.content}
</Dialog>
);
And I'm delivering handlers using context so that my modal could be open and closed everywhere.
// modalHandlersContext.js
const initialState = {
open: false,
content: null,
};
const ModalHandlersProvider = ({ children }) => {
const [modal, setModal] = useState(initialState);
const handlers = {
openModal: payload => setModal({ open: true, ...payload }),
closeModal: () => setModal(initialState),
};
return (
<ModalHandlersContext.Provider value={handlers}>
{children}
<CustomModal
open={modal.open}
onClose={handlers.closeModal}
content={modal.content}
></CustomModal>
</ModalHandlersContext.Provider>
);
};
When I want to open modal somewhere, I execute a function like this
const onLogin = () =>
openModal({
content: <h1>component</h1>,
});
That is, I pass a component. (I used h1 for a simple example)
Main subject
But when I close it, it's not clean.
I experimented on this to check when this happens.
It happens
With css, display something from props(same code as above)
const CustomModal = props => (
<Dialog {...props} sx={{ '& .MuiDialog-paper': { padding: '2em' } }}>
{props.content}
</Dialog>
);
It doesn't happen
Without css, display something from props
const CustomModal = props => (
<Dialog {...props}>
{props.content}
</Dialog>
);
2,3. With/Without css, display just plain text
const CustomModal = props => (
<Dialog {...props} sx={{ '& .MuiDialog-paper': { padding: '2em' } }}>
content
</Dialog>
);
const CustomModal = props => (
<Dialog {...props}>
content
</Dialog>
);
So after that, I tried using <DialogContent /> instead of css but It didn't work. But I have tried using <Modal /> instead of <Dialog /> and it hasn't caused any problems.
I wanna use <Dialog /> if possible but now can't find the cause.
Can anyone help?

Passing a single boolean useState between two pages using Material UI web components

I've been trying to pass in a boolean useState between two pages, using the best practice shown here: How to call useState from another Page?
My main objective is to show a success alert on a different page, once a form is successfully submitted on "./demo"
Demo.js holds a dialog with a submit button that sets the setSuccess to true.
import Alert from "./alert";
export default function AlertDialog() {
const [success, setSuccess] = React.useState(false); // <- Hides and Shows the Alert Message
const handleSubmit = () => {
return (
<Alert
setSuccess={() => {
setSuccess(true); // <- How I am trying to setSuccess to true.
}}
/>
);
};
return (
<DialogActions>
<Button
onClick={handleSubmit}
color="primary"
autoFocus
component={RouterLink}
to={"/"}
>
Submit
</Button>
</DialogActions>
);
Alert.js has an alert message that appears once success is set to true.
export default function Alerts(props) {
// const [open, setOpen] = React.useState(false);
const { success, setSuccess } = props;
return (
<div>
<Collapse in={success}>
<Alert
action={
<IconButton
aria-label="close"
color="inherit"
size="small"
onClick={() => {
setSuccess(false);
}}
>
<CloseIcon fontSize="inherit" />
</IconButton>
}
>
Form Successfully Submitted
</Alert>
</Collapse>
<Button
disabled={success}
variant="outlined"
component={RouterLink}
to={"/demo"}
>
Go Back to Submit Form
</Button>
</div>
)
;
}
Could someone explain how I can have the success alert appear after submission? If you wish to take a deeper dive, please visit here https://codesandbox.io/s/alert-test-qhkbg?file=/alert.js
I think what you're looking for here is passing state through React Router. Right now your alert isn't updating because your URL is changing when the submit button is pressed.
Check out this sandbox. I'm passing a message and a property to get the alert to render within the / route with different logic.
This is the key snippet:
<Button
color="primary"
autoFocus
component={RouterLink}
to={{
pathname: "/",
state: { message: "hello, i am a state message", open: true }
}}
>
Submit
</Button>
And then in your alert component at the url / you can:
const [open, setOpen] = React.useState(false);
React.useEffect(() => {
props.location.state?.open ? setOpen(true) : setOpen(false);
}, [props]);
// This message is what is being passed. Could be anything.
console.log(props.location.state);

How to stop useEffect from loading my modal multiple times?

I have a function based React view which has a modal that loads based on the presence of an item in the local storage:
function myView() {
...
function HelpModal() {
const [show, setShow] = useState(false);
useEffect(()=>{
let pop_status = localStorage.getItem('this-page-modal');
if(!pop_status){
setShow(true);
}
},[])
if(!setShow) return null;
const handleClose = () => {
localStorage.setItem('this-page-modal', '1');
setShow(false);
};
const handleShow = () => setShow(true);
return (
<>
<Button variant="primary" onClick={handleShow}>
Button text
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Body>
<p>Hey! This is a modal.</p>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
}
return (
<div>
<Container style={{marginTop: "1em"}}>
<Row>
<Col><h1>My View</h1></Col>
<Col><span style={{float: "right"}}> <HelpModal /></span></Col>
</Row>
</Container>
</div>
);
}
The problem: When I load the page, the modal flickers and loads 2 or 3 times.
How do I make it load only once? Any suggestions will be appreciated.
For the general case, you can use useLayoutEffect to make the update occur quicker, near instantaneously, instead of a bit after a repaint with useEffect.
Also, the if(!setShow) return null; line is completely superfluous because the useState setter function will always be a function, which is truthy.
useLayoutEffect(() => {
const pop_status = localStorage.getItem('this-page-modal');
if (!pop_status) {
setShow(true);
}
}, []);
But since the function inside the effect is completely synchronous, it'd make more sense to use the storage value as the initial value for the show state, instead of calling the state setter; you an remove the effect entirely.
const [show, setShow] = useState(!localStorage.getItem('this-page-modal'));
const handleClose = () => {
localStorage.setItem('this-page-modal', '1');
setShow(false);
};
const handleShow = () => setShow(true);
return (
// ...
Your setup could be causing re-renders based upon code not represented here so it's difficult to diagnose the exact issue. One thing to try might be to put a console.log statement in various function scopes to see how many times it's being re-rendered on load. Aside from that, (and concurring with previous answer) I think you might be able to simplify your code down a bit and get the same result by having the conditional render be straight from localStorage. Maybe something like:
function HelpModal() {
const [show, setShow] = useState(localStorage.getItem("this-page-modal"));
const handleClose = () => {
setShow(false);
localStorage.setItem("this-page-modal", false);
};
const handleShow = () => {
setShow(true);
localStorage.setItem("this-page-modal", true);
};
return (
<>
<Button variant="primary" onClick={handleShow}>
Button text
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Body>
<p>Hey! This is a modal.</p>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
}
function myView() {
return (
<>
<HelpModal />
</>
);
}

React (Nextjs): render table row to boostrap modal onclick

I have the following Nextjs page using React bootstrap modal and a MUI datatables component:
const Start = () => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const options = {
onTableInit: rowData,
onRowClick: rowData => Testing(rowData),
}
return (
<Layout>
<Modal show={show} onHide={handleClose}>
<Modal.Header>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>
Modal content
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
<MUIDataTable
title={"Datatable with modal onRowClick"}
data={data}
columns={columns}
options={options}
responsive="scrollFullHeight"
/>
</Layout>
)
}
export default Start;
The bootsrap modal works fine and opens onRowClick, I am also able to console log the rowData in a function when clicking a certain row:
function Testing(rowData) {
console.log(rowData)
);
I would like to achieve passing this rowData to the bootstrap modal accordingly when clicked on certain row.
I know I should be using the mapping, since it's an JSON object
{rowData.map(item => {
return <li key={item}>{item}</li>
}
)}
But how to integrate this into my current system? I get the error on rowData is not a function.

Why is my useState hook not holding the value of my variable?

I have a simple form presented with a modal that is intended to update my redux store with the value typed into the textbox.
I'm trying to use the useState hook to set the value of the textbox on change. When printing to the console I can see that the value of my variable - "note", is the correct value. However when trying to submit and passing "note" into my updateCheckpoint function, the value is undefined.
Additionally upon opening the dialog and submitting a second time, the note has the correct value.
function CompetencyCheckpoint(props)
{
const dispatch = useDispatch();
let [note, setNote] = useState();
function updateCheckpoint(note){
if(note != null){
console.log("Here is our note : " + note);
}else{
console.log("oh no, we didn't get the note");
}
}
console.log(note);
return (
<div className="checkpoint-container">
<i className="material-icons" onClick={()=>
dispatch(openDialog({
children: (
<React.Fragment>
<DialogTitle id="form-dialog-title">Add Note</DialogTitle>
<DialogContent>
<DialogContentText>
Enter a note.
</DialogContentText>
<TextField
id="filled-textarea"
label="New Note"
placeholder="Enter note here"
multiline
value={note}
onChange={(e) => setNote(e.target.value)}
margin="normal"
variant="filled"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={()=> dispatch(closeDialog())} color="primary">
Cancel
</Button>
<Button onClick={() => updateCheckpoint(note)} color="primary">
Add
</Button>
</DialogActions>
</React.Fragment>
)
}))}>
note_add
</i>
</div>);
}
export default (CompetencyCheckpoint);
The issue is that your JSX for the dialog content is captured at the time of opening the dialog and will not update as re-renders occur due to changing the note state. This is why #AhmadNoor's solution causes further issues -- it changes your TextField from uncontrolled to controlled, but it never receives the updated note value.
Your openDialog function should just control the open property on the Dialog. CompetencyCheckpoint should change to include the JSX of the entire Dialog directly rather than as an argument to the openDialog dispatch, so that it is included in re-renders due to changes to the note state.
Here's one way it could work (I'm assuming in my example that these are Material-UI components):
import React from "react";
import {
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
Button,
TextField
} from "#material-ui/core";
function CompetencyCheckpoint(props) {
const [open, setOpen] = React.useState(false);
const [note, setNote] = React.useState("");
function updateCheckpoint(note) {
if (note != null) {
console.log("Here is our note : " + note);
} else {
console.log("oh no, we didn't get the note");
}
}
console.log(note);
return (
<div className="checkpoint-container">
<i className="material-icons" onClick={() => setOpen(true)}>
note_add
</i>
<Dialog open={open} onClose={() => setOpen(false)}>
<DialogTitle id="form-dialog-title">Add Note</DialogTitle>
<DialogContent>
<DialogContentText>Enter a note.</DialogContentText>
<TextField
id="filled-textarea"
label="New Note"
placeholder="Enter note here"
multiline
value={note}
onChange={e => setNote(e.target.value)}
margin="normal"
variant="filled"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={() => setOpen(false)} color="primary">
Cancel
</Button>
<Button onClick={() => updateCheckpoint(note)} color="primary">
Add
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default CompetencyCheckpoint;
I was able to get the code working as expected. I'm not sure, maybe it had something to do with the way I was using the children: keyword, I'll check the docs to see what exactly that was doing. I was following a precedent I didn't fully understand.
Here is the solution I came up with. Basically I just added a state variable to control weather or not the dialog should show. I guess this also simplifies it so that i'm not calling redux to handle the Dialog toggle.
https://codesandbox.io/embed/focused-allen-cg1lq?fontsize=14
There may be further room for refactoring by making the Dialog it's own Component.
Thank you everyone who helped out, especially T.J and Ryan!

Categories

Resources