How to set focus to button in react and material ui on Dialog properly? - reactjs

When I used code :
<DialogActions>
<Button onClick={handleClose}>Disagree</Button>
<Button onClick={handleClose} autoFocus>
Agree
</Button>
</DialogActions>
it give me the error:
Line 35:51: The autoFocus prop should not be used, as it can reduce usability and accessibility for users jsx-a11y/no-autofocus
So I used different approach:
import { useState, useRef, useEffect } 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 PropTypes from 'prop-types';
export default function DialogYesNo(props) {
const { parentShowAlert, parentOnCloseDialog, dialogTitle, dialogText } = props;
const [open, setOpen] = useState(false);
useEffect(() => {
setOpen(parentShowAlert);
}, [parentShowAlert]);
const handleClose = () => {
setOpen(false);
parentOnCloseDialog();
};
const cancelButtonRef = useRef(null);
useEffect(() => {
if (open && cancelButtonRef.current) {
cancelButtonRef.current.focus();
}
}, [open]);
return (
<div>
<Dialog open={open} onClose={handleClose} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description">
<DialogTitle id="alert-dialog-title">{dialogTitle}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">{dialogText}</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} ref={cancelButtonRef}>
No
</Button>
<Button onClick={handleClose}>Agree</Button>
</DialogActions>
</Dialog>
</div>
);
}
DialogYesNo.propTypes = {
parentShowAlert: PropTypes.bool,
parentOnCloseDialog: PropTypes.func,
dialogTitle: PropTypes.string,
dialogText: PropTypes.string
};
But code cancelButtonRef.current.focus() is never fired, because cancelButtonRef is always null and button is not set as focused.
Do you know what is wrong in my code?

Related

How can a parent run a function inside a child which is not known in advance?

When using React 18, if I have a MUI Dialog with Save button and I insert in a TextItem inside it which has a form. How can I submit the form handler inside the inserted TextItem while clicking the Dialog action button "Save"? Note that the inserted TextItem is not known in advance and will be dynamic. So it could be PhotoItem, TextItem, RandomItem. I truly prefer managing the form state and submission handling in that child component. I have looked into refs before using useImperativeHandle but I am not sure if its the best solution.
TextItem.js
import React from 'react'
function TextItem() {
const submit = () => {
console.log('submit');
}
return (
<div>TextItem</div>
)
}
export default TextItem
ContentDialog.js
import { useEffect, useState } 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 TextItem from '../space/Screens/TextItem';
import useMediaQuery from "#mui/material/useMediaQuery";
export default function ContentDialog(props) {
const [toggle, setToggle] = useState(true);
const bigScreen = useMediaQuery((theme) => theme.breakpoints.up("md"));
const handleClose = () => {
setToggle(false);
}
const handleOpen = () => {
setToggle(true);
}
useEffect(() => {
setToggle(props.open);
}, [props.open]);
return (
<div>
<Dialog
fullScreen={!bigScreen}
open={toggle}
onClose={props.handleClose}
aria-labelledby="scroll-dialog-title"
aria-describedby="scroll-dialog-description"
>
<DialogTitle id="scroll-dialog-title">Subscribe</DialogTitle>
<DialogContent dividers={scroll === 'paper'}>
<TextItem/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleClose}>Save</Button>
</DialogActions>
</Dialog>
</div>
);
}

Multiple modal handle in react-js

I am new here and I saw some related answer but it will use another package and class components, i have functional component and use MUI Dialog for popup but the functionality is only for one modal but I have many modal my code
import React from 'react'
import Image from '../../assets/images/banner1.png'
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 Button from '#mui/material/Button';
export default function Popups() {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div className='image-data-grids'>
<div className='image-and-data-section'>
<img onClick={handleClickOpen} className='image-data-grid-image' src={Image} alt='Img' />
</div>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Content1
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Disagree</Button>
</DialogActions>
</Dialog>
<div className='image-and-data-section'>
<img onClick={handleClickOpen} className='image-data-grid-image' src={Image} alt='Img' />
</div>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent>
<DialogContentText id="alert-dialog-description">
content2
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Disagree</Button>
</DialogActions>
</Dialog>
</div>
)
}
here handleClickOpen and handleClose are the function for open and close for single modal, i have multiple modals. how customize these two function for multiple modal, i am new in react please help me to solve this issue, Thanks in advance
I know you are talking about having multiple state dependencies within a Component. I think you can do this
function StackOverflow() {
// We create a state dep that holds all our modals open/close state
const [myModals, setMyModals] = useState({
modalA: true,
modalB: false,
})
// This will return an api in which we can toggle, close or open a modal
// ie: set the boolean to true or false
const getModalHanlder = (modalName) => {
return {
isOpen: myModals[modalName],
open: () => setMyModals((state) => ({ ...state, [modalName]: true })),
close: () => setMyModals((state) => ({ ...state, [modalName]: false })),
toggle: () =>
setMyModals((state) => ({ ...state, modalA: !state[modalName] })),
}
}
const modalA = getModalHanlder("modalA")
// Here we invoke our function and pass on the desired modal prop name from
// which we desire to create an api
// We can then create this for another modal, modalB
const modalB = getModalHanlder("modalB")
return (
<div>
<b>MODAL_A: {`${modalA.isOpen}`}</b>
<br />
<button onClick={modalA.toggle}>TOGGLE</button>
<button onClick={modalA.close}>CLOSE</button>
<button onClick={modalA.open}>OPEN</button>
</div>
)
}
This is one approach another one can be to create an state for each modal, like
const [isOpenModalA, setIsOpenModalA] = useState(false)
// And this for each modal state
A fancy one can be to use a hook as useReducer and update each modal state that depends on actions you dispatch, https://reactjs.org/docs/hooks-reference.html#usereducer

Adding Validation and Function to Input using React and MUI

I am creating a cat APP, that pulls a request from thecatapi, I want my submit button to load my ImageGrid.jsx when they input the correct link for example:
in the dialog box you put:
https://api.thecatapi.com/v1/images/search
then it must have a submit loading function and take you to the images of the cats:
what i have is this:
import React, { useState } from 'react';
import PropTypes from 'prop-types'
import getCat from './ImageGrid'
// Material UI
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogTitle from '#material-ui/core/DialogTitle';
import Input from '#material-ui/core/Input'
import Dialog from '#material-ui/core/Dialog';
import Button from '#material-ui/core/Button';
import Grid from '#material-ui/core/Grid';
const DialogBox = ({ ariaLabel }) => {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
// const handleGrid = () => {
// React.useState(true)
// }
return (
<div className='modal'>
<Grid container justifyContent='center'>
{/* Button To Open Dialog Box */}
<Button
style={{border: '1px solid #ebc340', color: '#ebc340'}}
variant="outlined"
color="secondary"
onClick={handleClickOpen}>
Welcome to my Case Study, Click to begin
</Button>
</Grid>
{/* Dialog Box Content */}
<Dialog
className='dialog-btn'
open={open}
onClose={handleClose}>
<DialogTitle>
To begin the application, please insert your URL below:
</DialogTitle>
<DialogContent>
<Input
placeholder="Enter Your Link Here"
inputProps={ariaLabel}
fullWidth/>
</DialogContent>
{/* Dialog Box Actions */}
<DialogActions>
<Button
onClick={handleClose}
color="secondary">
Cancel
</Button>
<Button
onClick={ getCat }
color='primary'
autoFocus
type='submit'>
{/* onSubmit={handleGrid} */}
Submit
</Button>
</DialogActions>
</Dialog>
</div>
);
}
Input.inputProps = {
ariaLabel: 'Please insert your URL below',
tertiaryColor: 'tertiary'
}
Input.inputTypes = {
ariaLabel: PropTypes.string.isRequired,
tertiaryColor: PropTypes.string.isRequired,
};
export default DialogBox
and then it needs to load this:
/
/ Fetch data from
import React, { useCallback, useEffect, useState } from "react";
const url = "https://api.thecatapi.com/v1/images/search?limit=9";
function ImageGrid() {
const [catUrls, setCatUrls] = useState();
const getCat = useCallback(() => {
console.log("Hello World");
let catImageUrlList = [];
// fetch http request
fetch(url)
.then((res) => res.json()) //gives data in json
.then((cats) => {
console.log("Cats: ", cats);
for (let i = 0; i < cats.length; i++) {
catImageUrlList.push(cats[i].url);
}
setCatUrls(catImageUrlList);
})
.catch((error) => {
console.log("Error: ", error);
});
}, [setCatUrls]);
useEffect(() => {
console.log("Loading your feline friends....");
getCat();
}, [getCat]);
return (
<>
<h1>Look At These Beautiful Kitty's!</h1>
{catUrls ? (
catUrls.map((catUrl) => <img src={catUrl} alt={"kitty"} />)
) : (
<p>Loading...</p>
)}
<button onClick={getCat}>Refresh</button>
</>
);
}
export default ImageGrid;

How to prevent re-render when using react material ui dialogs

I'm using modals from the react-material-ui library for a project, and I noticed a side effect when trying to open/close the dialog component. Try this code (code sandbox):
import { useState } from "react";
import moment from "moment";
import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import DialogTitle from "#material-ui/core/DialogTitle";
import DialogActions from "#material-ui/core/DialogActions";
import "./styles.css";
export default function App() {
const [open, setOpen] = useState(false);
const timeStamp = moment().format("HH:mm:ss SS");
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div className="App">
<h1>Mui Dialog</h1>
<h2>Rendered on {timeStamp}</h2>
<Button variant="outlined" color="primary" onClick={handleOpen}>
Open simple dialog
</Button>
<Dialog open={open}>
<DialogTitle>This is a simple dialog</DialogTitle>
<DialogActions>
<Button onClick={handleClose} color="primary" autoFocus>
Close
</Button>
</DialogActions>
</Dialog>
</div>
);
}
You'll see that clicking on the button open will cause a re-render and closing the dialog will have the same effect, it's normal because state changed after calling hooks. This is often undesirable, I don't want to re-render the page when opening or after closing.
So I tried to use the following solution, based of refs (code sandbox):
import { useState, useRef, forwardRef, useImperativeHandle } from "react";
import moment from "moment";
import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import DialogTitle from "#material-ui/core/DialogTitle";
import DialogActions from "#material-ui/core/DialogActions";
import "./styles.css";
const SimpleDialog = forwardRef(({ title }, ref) => {
const [open, setOpen] = useState(false);
const innerRef = useRef();
const handleClose = () => {
setOpen(false);
};
useImperativeHandle(ref, () => ({
openDialog: () => setOpen(true),
closeDialog: () => setOpen(false)
}));
return (
<Dialog open={open} ref={innerRef}>
<DialogTitle>{title}</DialogTitle>
<DialogActions>
<Button onClick={handleClose} color="primary" autoFocus>
Close
</Button>
</DialogActions>
</Dialog>
);
});
export default function App() {
const timeStamp = moment().format("HH:mm:ss SS");
const dialogRef = useRef();
const handleOpen = () => {
dialogRef.current.openDialog();
};
return (
<div className="App">
<h1>Mui Dialog</h1>
<h2>Rendered on {timeStamp}</h2>
<Button variant="outlined" color="primary" onClick={handleOpen}>
Open simple dialog
</Button>
<SimpleDialog ref={dialogRef} title="This is a simple dialog" />
</div>
);
}
My question is: is this a correct approach to solve the problem of unwanted re-renders in the case of react-material-ui modals ?
Regards.

ReactJS Modal with Material UI

I'm trying to make a reusable confirmation modal with Material UI but when I press CANCEL or OK the modal does not close.
The code is here:
https://codesandbox.io/s/lucid-hoover-sput6?file=/src/App.js
I can't figure it out why the pop don't dissapear.
LE: added the code here so it remains
ConfirmModal.js
import React from "react";
import { Button } from "#material-ui/core";
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";
const ConfirmModal = (props) => {
const { content, open, setOpen, onConfirm } = props;
return (
<Dialog
open={open}
onClose={() => setOpen(false)}
aria-labelledby="dialog-title"
>
<DialogContent>
<DialogContentText>{content}</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={() => setOpen(false)} color="primary">
Cancel
</Button>
<Button
onClick={() => {
setOpen(false);
onConfirm();
}}
color="primary"
>
OK
</Button>
</DialogActions>
</Dialog>
// </div>
);
};
export default ConfirmModal;
App.js
import React, { useState } from "react";
import { IconButton } from "#material-ui/core";
import { Input as InputIcon } from "#material-ui/icons";
import ConfirmModal from "./ConfirmModal";
export default function App() {
const [confirmOpen, setConfirmOpen] = useState(false);
const handleLogout = () => {
console.log("this hould logout the user");
};
return (
<div className="App">
<h2>Press the button below so the confirmation modal appears </h2>
<IconButton color="inherit" onClick={() => setConfirmOpen(true)}>
<InputIcon />
<ConfirmModal
content="Are you sure you want to leeeave us ?"
open={confirmOpen}
setOpen={setConfirmOpen}
onConfirm={handleLogout}
/>
</IconButton>
</div>
);
}
Move the modal out of the button. The Modal's cancel/confirm/backdrop click events are propagating (bubbling) up to the open button (IconButton) and its onClick handler is just reopening the modal by setting confirmOpen state true.
export default function App() {
const [confirmOpen, setConfirmOpen] = useState(false);
const handleLogout = () => {
console.log("this hould logout the user");
};
return (
<div className="App">
<h2>Press the button below so the confirmation modal appears </h2>
<IconButton color="inherit" onClick={() => setConfirmOpen(true)}>
<InputIcon />
</IconButton>
<ConfirmModal
content="Are you sure you want to leeeave us ?"
open={confirmOpen}
setOpen={setConfirmOpen}
onConfirm={handleLogout}
/>
</div>
);
}

Resources