Open Material UI Dialogs from Parent Component? - javascript

I need to open two seperate Material UI dialogs (Terms & Privacy) from their parent component which is a material UI 'simple menu'. I have them imported and nested in the parent already, I just don't know how to get them to open from their parent while being seperate files. I played around with this similar question for way too long but couldn't get it working. Thanks.
Parent component (Menu):
import React from 'react';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import IconButton from '#material-ui/core/IconButton';
import MoreVertIcon from '#material-ui/icons/MoreVert';
import Terms from './Terms';
import Privacy from './Privacy'
const More = () => {
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div>
<IconButton
aria-label="more"
aria-controls="simple-menu"
aria-haspopup="true"
onClick={handleClick}
edge='end'
>
<MoreVertIcon />
</IconButton>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={handleClose}>Terms of Use</MenuItem>
<MenuItem onClick={handleClose}>Privacy Policy</MenuItem>
</Menu>
<Terms />
<Privacy />
</div>
);
}
export default More;
Child component 1 ('Terms' dialog):
import React from 'react';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import Button from '#material-ui/core/Button';
const Terms = () => {
const [openTerms, setOpen] = React.useState(false);
const openTermsDialog = () => {
setOpen(true);
};
const handleCloseTerms = () => {
setOpen(false);
};
return (
<Dialog
open={openTerms}
onClose={handleCloseTerms}
aria-labelledby="terms-dialog-title"
aria-describedby="terms-dialog-description"
>
<DialogTitle id="terms-dialog-title">{"Terms of Use"}</DialogTitle>
<DialogContent>
<DialogContentText id="terms-dialog-description">
Terms go here
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseTerms} color="primary" autoFocus>
Agree
</Button>
</DialogActions>
</Dialog>
)
}
export default Terms;
Child component 2 ('Privacy' dialog):
import React from 'react';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import Button from '#material-ui/core/Button';
const Privacy = () => {
const [openPrivacy, setOpen] = React.useState(false);
const handleOpenPrivacy = () => {
setOpen(true);
};
const handleClosePrivacy = () => {
setOpen(false);
};
return (
<Dialog
open={openPrivacy}
onClose={handleClosePrivacy}
aria-labelledby="privacy-dialog-title"
aria-describedby="privacy-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Privacy Policy"}</DialogTitle>
<DialogContent>
<DialogContentText id="privacy-dialog-description">
Privacy policy goes here
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClosePrivacy} color="primary" autoFocus>
Agree
</Button>
</DialogActions>
</Dialog>
)
}
export default Privacy;

Just lift up the state from the dialog components to the parent component, and pass it down to the child components.
const More = () => {
const [anchorEl, setAnchorEl] = React.useState(null);
const [openDialogName, setOpenDialog] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const openTermsDialog = () => {
setOpenDialog('TERMS');
handleClose();
};
const openPrivacyDialog = () => {
setOpenDialog('PRIVACY');
handleClose();
};
const closeDialog = () => {
setOpenDialog(null);
};
return (
<div>
{/* ... */}
<MenuItem onClick={openTermsDialog}>Terms of Use</MenuItem>
<MenuItem onClick={openPrivacyDialog}>Privacy Policy</MenuItem>
{/* ... */}
<Terms open={openDialogName === 'TERMS'} onClose={closeDialog} />
<Privacy open={openDialogName === 'PRIVACY'} onClose={closeDialog} />
</div>
);
}
export default More;
And in Privacy for example (and the same idea for Terms as well):
const Privacy = ({ open, onClose }) => {
return (
<Dialog
open={open}
onClose={onClose}
>
{/* ... */}
);
};

Related

How to add a new row with datas from another component in Materials UI?

wanted to add a new element in a table with Materials UI,using a component that adds a new row.The component takes data from user and the idea is to send this data to the Table component.How can I transfer this data from DialogBoxAddPost to Table component and how to mix the data from the API and the new user added data.
Thank you!
Table component:
import * as React from 'react';
import { styled } from '#mui/material/styles';
import Table from '#mui/material/Table';
import TableBody from '#mui/material/TableBody';
import TableCell, { tableCellClasses } from '#mui/material/TableCell';
import TableContainer from '#mui/material/TableContainer';
import TableHead from '#mui/material/TableHead';
import TableRow from '#mui/material/TableRow';
import Paper from '#mui/material/Paper';
import { Button } from '#mui/material';
import './App.css';
import { useEffect, useState } from "react";
import DialogBoxEdit from './DialogBoxEdit';
export default function Tableall() {
const [posts, setPosts] = useState([])
const fetchData = () => {
fetch("https://jsonplaceholder.typicode.com/posts").then(response => {
return response.json()
})
.then((data) => {
setPosts(data.slice(0, 6));
});
};
useEffect(() => {
fetchData()
}, []);
const handleDelete = (postIndex) => {
setPosts((prevPosts) =>
prevPosts.filter((_, index) => index !== postIndex)
);
};
return (
<TableContainer sx={{
marginLeft:'auto',
width:'max-content',
marginRight:'auto',
}}component={Paper}>
<Table sx={{ minWidth: 700,tableLayout:'auto' }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell sx={{width:250}}>Title</StyledTableCell>
<StyledTableCell align="center" sx={{width:500}}>Description</StyledTableCell>
<StyledTableCell align="center" sx={{width:100}}></StyledTableCell>
<StyledTableCell align="center"sx={{width:100}}></StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{posts.map((post,postIndex) => (
<StyledTableRow key={post.id}>
<StyledTableCell component="th" scope="row">{post.title}</StyledTableCell>
<StyledTableCell align="center">{post.body}</StyledTableCell>
<StyledTableCell align="center"><DialogBoxEdit dataParent1={post.title} dataParent2={post.body} /></StyledTableCell>
<StyledTableCell align="center"><Button variant="outlined" color="error" onClick={() => handleDelete(postIndex)}>Delete</Button></StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
Dialogbox to add a new elements in a table.
import * as React from 'react';
import Button from '#mui/material/Button';
import Dialog from '#mui/material/Dialog';
import DialogActions from '#mui/material/DialogActions';
import DialogContent from '#mui/material/DialogContent';
import DialogContentText from '#mui/material/DialogContentText';
import DialogTitle from '#mui/material/DialogTitle';
import useMediaQuery from '#mui/material/useMediaQuery';
import { useTheme } from '#mui/material/styles';
import Table from './TableAll';
import TextField from '#mui/material/TextField';
import Divider from '#mui/material/Divider';
import {useState} from 'react';
export default function ResponsiveDialog() {
const [open, setOpen] = React.useState(false);
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('md'));
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const [message, setMessage] = useState('');
const [message2, setMessage2] = useState('');
const handleChange = event => {
setMessage(event.target.value);
};
const handleChange2 = event => {
setMessage2(event.target.value);
};
const handleClick = event => {
event.preventDefault();
console.log(message,message2);
setOpen(false);
};
return (
<div>
<Button variant="contained" sx={{right:477,top:-10,height:48}} color='warning' size='large' onClick={handleClickOpen}>
Add new +
</Button>
<Dialog
fullScreen={fullScreen}
open={open}
onClose={handleClose}
aria-labelledby="responsive-dialog-title"
fullWidth
maxWidth="sm"
>
<DialogTitle id="responsive-dialog-title">{"Add new post"}
</DialogTitle>
<DialogContent>
<Divider variant="middle" />
<DialogContentText>
Title
</DialogContentText>
<TextField id="outlined-basic" onChange={handleChange} value={message} name="message" label="Title" variant="outlined" style = {{width: 500}} />
<DialogContentText>
Description
</DialogContentText>
<TextField id="outlined-basic" onChange={handleChange2} value={message2} multiline rows={4} label="Description" variant="outlined" style = {{width: 500}} />
</DialogContent>
<DialogActions>
<Button autoFocus variant="outlined" color='error' onClick={handleClose}>
Exit
</Button>
<Button color='success' variant="outlined" onClick={handleClick} autoFocus>
Add
</Button>
</DialogActions>
</Dialog>
</div>
);
}

cannot get default value of datetime picker to work properly

i'm using the datetime picker from: https://material-ui.com/components/pickers/ I want the default value to come from props.note.NoteDate but I can't seem to set or even see the default value as Note date has the annoying format string I cannot remove. Any help is appreciated.
here is my react control:
import React from 'react';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
export default function EditNoteDialog(props) {
const [open, setOpen] = React.useState(true);
const [notetext, setNoteText] = React.useState();
const [notedate, setNoteDate] = React.useState();
React.useEffect (() => {
//notedate is a string like 2021-09-01T01:34:00.000Z
setNoteDate(props.note.NoteDate);
setNoteText(props.note.NoteText);
},[props.note.NoteDate, props.note.NoteText])
let handleNoteTextChange = (e) => {
setNoteText(e.currentTarget.value);
};
let handleNoteDateChange = (e) => {
console.log(e.currentTarget.value);
setNoteDate(e.currentTarget.value);
};
const handleClose = () => {
let editednote = {
NoteID: props.note.NoteID,
NoteText: notetext,
NoteType: props.note.NoteType,
NoteDate: notedate
};
//console.log(editednote.NoteDate)
setOpen(false);
props.SaveEditedNote(editednote);
};
const handleCancel = () => {
setOpen(false);
props.handleCancel(0);
};
return (
<div>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Edit Note</DialogTitle>
<DialogContent>
<DialogContentText>
Edit Note
</DialogContentText>
{/* <TextField
autoFocus
margin="dense"
id="noteid"
label="NoteID"
defaultValue = {noteid}
fullWidth
/> */}
<TextField
autoFocus
margin="dense"
id="notetext"
label="NoteText"
defaultValue = {notetext}
fullWidth
onChange = {handleNoteTextChange}
/>
<TextField
autoFocus
type = 'datetime-local'
margin="dense"
id="notedate"
defaultValue = "2021/8/15T03:12:05"
//value = {notedate}
label="Note Date"
fullWidth
onChange = {handleNoteDateChange}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleCancel} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
</div>
);
}
per briosheje's comment datetime-local's default value doesn't seem to allow for milliseconds. Fortunately I didn't either so I just chopped the milliseconds off.
defaultValue = {props.note.NoteDate.split(".")[0]}

Dynamic input fields in React using functional component

I am trying to create dynamic input fields in my react application(using Material UI) on the click of a button. Whenever the add button is clicked, it creates a new input field inside a dialog box but I can't seem to enter any values in the text-field. Following is my app.js file -
import React from "react";
import "./styles.css";
import Button from "#material-ui/core/Button";
import TextField from "#material-ui/core/TextField";
import Dialog from "#material-ui/core/Dialog";
import DialogActions from "#material-ui/core/DialogActions";
import DialogContent from "#material-ui/core/DialogContent";
import DialogContentText from "#material-ui/core/DialogContentText";
import DialogTitle from "#material-ui/core/DialogTitle";
import IconButton from "#material-ui/core/IconButton";
import DeleteIcon from "#material-ui/icons/Delete";
import { Box, Grid } from "#material-ui/core";
export default function App() {
const [open, setOpen] = React.useState(false);
const [values, setValues] = React.useState([]);
const [text, setText] = React.useState("");
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
setValues([]);
};
const handleChangeText = (e) => {
setText(e.target.value);
};
const addValue = () => {
setValues([...values, ""]);
};
const handleValueChange = (index, e) => {
values[index] = e.target.value;
console.log(values);
setValues(values);
};
const deleteValue = (jump) => {
setValues(values.filter((j) => j !== jump));
};
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Create
</Button>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">New Dialog</DialogTitle>
<DialogContent>
<DialogContentText>Sample Text.</DialogContentText>
<TextField
autoFocus
margin="dense"
value={text}
onChange={handleChangeText}
label="Text"
fullWidth
/>
{values.map((jump, index) => (
<Box key={"jump" + index}>
<Grid container spacing={1} alignItems="flex-end">
<Grid item xs={10}>
<TextField
autoFocus
margin="dense"
label="Value"
value={jump || ""}
onChange={(e) => handleValueChange(index, e)}
fullWidth
/>
</Grid>
<Grid item xs={2}>
<div
className="font-icon-wrapper"
onClick={() => deleteValue(jump)}
>
<IconButton aria-label="delete">
<DeleteIcon />
</IconButton>
</div>
</Grid>
</Grid>
</Box>
))}
</DialogContent>
<Button onClick={addValue} color="primary">
Add
</Button>
<DialogActions>
<Button onClick={handleClose} variant="contained" color="secondary">
Cancel
</Button>
<Button onClick={handleClose} variant="contained" color="primary">
Create
</Button>
</DialogActions>
</Dialog>
</div>
);
}
Here is the code sandbox link.
I have a simple text field inside the dialog box also that works perfectly. The problem is with the dynamic fields. I don't know what mistake I am making in this code. Someone please help me out. Thanks in advance
You a have problem in the function handleValueChanges, don't use mutation use immutable data.
the correct code:
const handleValueChange = (index, e) => {
const updatedValues = values.map((value, i) => {
if(i === index) {
return e.target.value
}else {
return value
}
})
setValues(updatedValues);
};
You can test from here: https://codesandbox.io/s/dynamic-textfield-forked-gck25?file=/src/App.js

findDOMNode is deprecated in StrictMode React material UI

I am getting this error on my React material UI project
By looking at the error I guess it comes somewhere inside the Drawer.js component.
This is my full Drawer.js component
import React, { Fragment, useState } from "react";
import clsx from "clsx";
import { makeStyles, useTheme } from "#material-ui/core/styles";
import Drawer from "#material-ui/core/Drawer";
import { List, Collapse } from "#material-ui/core";
import Divider from "#material-ui/core/Divider";
import ListItem from "#material-ui/core/ListItem";
import IconButton from "#material-ui/core/IconButton";
import ListItemText from "#material-ui/core/ListItemText";
import { toggleDrawer } from "../../store/actions/authActions";
import ExpandLess from "#material-ui/icons/ExpandLess";
import ExpandMore from "#material-ui/icons/ExpandMore";
import ChevronLeftIcon from "#material-ui/icons/ChevronLeft";
import ChevronRightIcon from "#material-ui/icons/ChevronRight";
// import ClickAwayListener from '#material-ui/core/ClickAwayListener'
import { useHistory } from 'react-router-dom';
import { connect } from "react-redux";
import { withRouter } from "react-router";
const useStyles = makeStyles({
list: {
width: 250,
},
fullList: {
width: "auto",
},
});
function TemporaryDrawer(props) {
const classes = useStyles();
const theme = useTheme();
// const [open, setOpen] = React.useState(false);
const [openIndex, setOpenIndex] = useState(0);
let history = useHistory();
// const handleDrawerOpen = () => {
// setOpen(true);
// };
const handleDrawerClose = () => {
props.toggleDrawer();
};
const handleClick = (index) => {
if (openIndex === index) {
setOpenIndex(-1);
} else {
setOpenIndex(index);
}
};
const onToggle = () => (event) => {
if (
event.type === "keydown" &&
(event.key === "Tab" || event.key === "Shift")
) {
return;
}
props.toggleDrawer();
};
const onRoute = (path) => {
// props.history.push(path);
history.push(path);
props.toggleDrawer();
};
const list = (anchor) => (
<div
className={clsx(classes.list, {
[classes.fullList]: anchor === "top" || anchor === "bottom",
})}
role="presentation"
>
<div className={classes.drawerHeader}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</div>
<Divider />
<List>
{props.permissions.map((route, index) => (
<Fragment key={index}>
<ListItem button onClick={(e) => handleClick(index)}>
<ListItemText primary={route.name} />
{index === openIndex ? <ExpandLess /> : <ExpandMore />}
</ListItem>
{route.children.length && (
<Collapse
in={openIndex === index ? true : false}
timeout="auto"
unmountOnExit
>
<List component="div" disablePadding>
{route.children.map((child, idx) => (
<ListItem
button
className={classes.nested}
key={idx}
onClick={() => onRoute(child.path)}
>
<ListItemText primary={child.name} />
</ListItem>
))}
</List>
<Divider />
</Collapse>
)}
</Fragment>
))}
</List>
{/* <Divider /> */}
</div>
);
return (
<div>
{props.token && (
<Drawer
anchor="left"
open={props.isDrawerOpen}
onClose={onToggle("left", false)}
variant="persistent"
>
{list("left")}
</Drawer>
)}
</div>
);
}
const mapStateToProps = (state) => {
return {
isDrawerOpen: state.auth.isDrawerOpen,
token: state.auth.token,
permissions: state.auth.routes,
};
};
export default withRouter(
connect(mapStateToProps, { toggleDrawer })(TemporaryDrawer)
);
I go through this error and some say this is a problem in MUI library and no way to fix this. But I believe there must be a workaround for this. This causes serious problems for UI.
Where this error comes from and what can I do to fix this?
Any help!
Thanks in advance. =)
I use material UI 4.11.0
and react 16
You only need to create a new childComponent with react.forwardRef passing respective props and refs:
const ChildComponent = React.forwardRef((props, ref) =>
<div ref={ref}>
<yourChildcomponent {...props} />
</div>
);
In your render change the name of the original Child for the new:
<ChildComponent... />

React Material UI + React Router >>>> I cannot use Redirect inside a onClick function in a button

I am trying to trigger the Redirect React Dom
that is my button component in the handleMenuItemClick() function. But nothing happens.
I have tried a bunch of stuff but but still no success.
How can I make the both work together? My best try was to make a function that return the Redirect component as I saw in one post around, but still no success.
My Code:
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { Grid, Button, ButtonGroup, ArrowDropDownIcon, ClickAwayListener, Grow, Paper, Popper, MenuItem, MenuList, Link } from '#material-ui/core/Grid';
const SplitButton = (props) => {
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
const [selectedIndex, setSelectedIndex] = React.useState(1);
const myGroups = props.myGroups
const handleMenuItemClick = (event, index) => {
setSelectedIndex(index);
setOpen(false);
return <Redirect to={`/groups/${index}`} />
};
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setOpen(false);
};
return (
<>
<ButtonGroup variant="contained" color="primary" ref={anchorRef} aria-label="split button">
<Button onClick={null}>My Groups</Button>
<Button
color="primary"
size="small"
aria-controls={open ? 'split-button-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-label="select merge strategy"
aria-haspopup="menu"
onClick={handleToggle}
>
<ArrowDropDownIcon />
</Button>
</ButtonGroup>
<Popper open={open} anchorEl={anchorRef.current} role={undefined} transition disablePortal>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList id="split-button-menu">
{ myGroups.map((group) => (
<MenuItem
key={group.id}
onClick={(event) => handleMenuItemClick(event, group.id)}
>
{group.title}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</>
);
}
export default SplitButton
You can redirect user via 2 methods: useHistory or <Redirect />
useHistory hook
If you want to redirect the user directly on click, you can treat the code imperatively and tell React what to do:
const history = useHistory();
const handleMenuItemClick = (event, index) => {
setSelectedIndex(index);
setOpen(false);
history.push(`/groups/${index}`)
};
More info https://reactrouter.com/web/api/Hooks/usehistory
Redirect component
Or if you feel more comfortable using React's default declarative model, you can say what's changed and allow your code to react to this change:
const [redirectUrl, setRedirectUrl] = useState('')
const handleMenuItemClick = (event, index) => {
setSelectedIndex(index);
setOpen(false);
setRedirectUrl(`/groups/${index}`)
};
if (redirectUrl) {
return <Redirect to={redirectUrl} />
}
return (
<>
<ButtonGroup variant="contained" color="primary" ref={anchorRef} aria-label="split button">
<Button onClick={null}>My Groups</Button>
<Button
...
More info https://reactrouter.com/web/api/Redirect

Categories