I'm learning how to use Material-ui for ReactJS, I don't understand what is the "TransitionProps" in this example code below:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Popper from '#material-ui/core/Popper';
import Fade from '#material-ui/core/Fade';
const useStyles = makeStyles((theme) => ({
paper: {
border: '1px solid',
padding: theme.spacing(1),
backgroundColor: theme.palette.background.paper,
},
}));
export default function TransitionsPopper() {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const open = Boolean(anchorEl);
const id = open ? 'transitions-popper' : undefined;
return (
<div>
<button aria-describedby={id} type="button" onClick={handleClick}>
Toggle Popper
</button>
<Popper id={id} open={open} anchorEl={anchorEl} transition>
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<div className={classes.paper}>The content of the Popper.</div>
</Fade>
)}
</Popper>
</div>
);
}
What will be passed into "TransitionProps"?
Here is the source code on Codesandbox
Hoping someone will explain some more on this syntax.
Thanks.
Related
I am trying to trigger two functions from a parent component by using forwardRef & useImperativeHandle. My code is below. I am not sure where I am going wrong but my code is failing to compile with the errors below. Please can someone advise? I thought i have defined the two functions in question that the compiler is complaining about. have I got the syntax wrong?
const SimpleBackdrop = React.forwardRef((props, ref) => {
const [open, setOpen] = React.useState(false);
React.useImperativeHandle(ref, () => ({
handleClose() {
setOpen(false);
},
handleToggle() {
setOpen(!open);
},
}));
return (
<div>
<Button onClick={handleToggle}>Show backdrop</Button>
<Backdrop
sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.drawer + 1 }}
open={open}
onClick={handleClose}
>
<CircularProgress color="inherit" />
</Backdrop>
</div>
);
});
export default SimpleBackdrop;
I am getting the following error in the console:
Failed to compile.
[eslint]
src/components/myApp/SimpleBackdrop.js
Line 44:24: 'handleToggle' is not defined no-undef
Line 48:18: 'handleClose' is not defined no-undef
I have alos tried to re-write is as below, but still getting the same compile error:
import * as React from 'react';
import Backdrop from '#mui/material/Backdrop';
import CircularProgress from '#mui/material/CircularProgress';
import Button from '#mui/material/Button';
const SimpleBackdrop = (props, ref) => {
const [open, setOpen] = React.useState(false);
React.useImperativeHandle(ref, () => ({
handleClose: () => setOpen(false),
handleToggle: () => setOpen(!open)
}));
return (
<div>
<Button onClick={handleToggle}>Show backdrop</Button>
<Backdrop
sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.drawer + 1 }}
open={open}
onClick={handleClose}
>
<CircularProgress color="inherit" />
</Backdrop>
</div>
);
}
export default React.forwardRef(SimpleBackdrop);
Try this:
const SimpleBackdrop = React.forwardRef(({...props}, ref) => {
const [open, setOpen] = React.useState(false);
const handleClose = () => {
setOpen(false);
};
const handleToggle = () => {
setOpen(!open);
};
React.useImperativeHandle(ref, () => ({
handleClose, handleToggle
}));
return (
<div>
<Button onClick={handleToggle}>Show backdrop</Button>
<Backdrop
sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.drawer + 1 }}
open={open}
onClick={handleClose}
>
<CircularProgress color="inherit" />
</Backdrop>
</div>
);
});
export default SimpleBackdrop;
How do I get the Backdrop to cover the button?
No matter what I do, the buttons appear as if they were above the backdrop, I can't them move them behind it.
See code:
import React from "react";
import { Backdrop, Button } from "#material-ui/core";
import CircularProgress from "#material-ui/core/CircularProgress";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({});
export default function App() {
const classes = useStyles();
const [test, setTest] = React.useState(true);
return (
<div className={classes.parent}>
<Backdrop className={classes.backdrop} open={test}>
<CircularProgress color="inherit" />
</Backdrop>
<Button
variant="contained"
color="secondary"
onClick={() => {
setTest(test ? false : true);
}}
>
I should be behind the backdrop (click me)
</Button>
</div>
);
}
You need to give the backdrop a higher z-index
const useStyles = makeStyles((theme) => ({ backdrop: { zIndex: theme.zIndex.drawer + 1, color: '#fff', }, }));
This should fix it, just check the docs.
I keep getting a blank avatarlistitem when I click add for the first time and then after that the appropriate item will display after the second click. If I add a new URL and click the third time the item won't appear until the fourth click. I am most likely doing something wrong with my useEffect and state values.
import React, { useState, useEffect } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import List from '#material-ui/core/List';
import Divider from '#material-ui/core/Divider';
import AvatarListItem from './AvatarListItem';
import AddIcon from '#material-ui/icons/Add';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import axios from 'axios';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
justifyContent: 'space-between',
flexDirection: 'column',
flexWrap: 'wrap',
backgroundColor: theme.palette.background.paper,
},
inline: {
display: 'inline',
},
formControl: {
width: '100%',
margin: theme.spacing(1),
marginBottom: '50px',
minWidth: 120,
},
extendedIcon: {
margin: '10px',
marginRight: theme.spacing(1),
},
}));
export default function AvatarList() {
const classes = useStyles();
const [url, setUrl] = useState('');
const [itemDetails, setItemDetails] = useState({});
const [items, setItems] = useState([]);
const [search, setSearch] = useState('');
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`http://127.0.0.1:5000/api/resources/products?url=${url}`,
)
setItemDetails(result.data[0]);
}
fetchData()
}, [search])
const addItem = (itemDetails) => {
const newItems = [...items, itemDetails];
setItems(newItems);
};
let handleSubmit = (e) => {
e.preventDefault();
console.log(itemDetails);
addItem(itemDetails);
};
let removeItem = (index) => {
const newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};
return (
<div>
<form className={classes.formControl} onSubmit={e => handleSubmit(e)}>
<TextField id="outlined-basic" label="Amazon Url" variant="outlined" name='newItem' onChange={e => setUrl(e.target.value)} value={url} />
<Button variant="contained" color="primary" type="submit" value="Submit" onClick={() => setSearch(url)}>
<AddIcon className={classes.extendedIcon} />
</Button>
</form>
<List className={classes.root}>
{items.map((item, index) => (
<>
<AvatarListItem
itemDetails={item}
key={index}
index={index}
removeItem={removeItem}
/>
<Divider variant="middle" component="li" />
</>
))}
</List>
</div >
);
}
A better approach may be to purely rely on the onSubmit callback instead of relying on the useEffect which may run more often than needed. Also, it doesn't look like you need to use the search or itemDetails state at all.
import React, { useState, useEffect } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import List from '#material-ui/core/List';
import Divider from '#material-ui/core/Divider';
import AvatarListItem from './AvatarListItem';
import AddIcon from '#material-ui/icons/Add';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import axios from 'axios';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
justifyContent: 'space-between',
flexDirection: 'column',
flexWrap: 'wrap',
backgroundColor: theme.palette.background.paper,
},
inline: {
display: 'inline',
},
formControl: {
width: '100%',
margin: theme.spacing(1),
marginBottom: '50px',
minWidth: 120,
},
extendedIcon: {
margin: '10px',
marginRight: theme.spacing(1),
},
}));
export default function AvatarList() {
const classes = useStyles();
const [url, setUrl] = useState('');
const [items, setItems] = useState([]);
const addItem = (itemDetails) => {
const newItems = [...items, itemDetails];
setItems(newItems);
};
let handleSubmit = async (e) => {
e.preventDefault();
const result = await axios(
`http://127.0.0.1:5000/api/resources/products?url=${url}`,
)
addItem(result.data[0]);
};
let removeItem = (index) => {
const newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};
return (
<div>
<form className={classes.formControl} onSubmit={handleSubmit}>
<TextField id="outlined-basic" label="Amazon Url" variant="outlined" name='newItem' onChange={e => setUrl(e.target.value)} value={url} />
<Button variant="contained" color="primary" type="submit">
<AddIcon className={classes.extendedIcon} />
</Button>
</form>
<List className={classes.root}>
{items.map((item, index) => (
<React.Fragment key={index}>
<AvatarListItem
itemDetails={item}
key={index}
index={index}
removeItem={removeItem}
/>
<Divider variant="middle" component="li" />
</React.Fragment>
))}
</List>
</div >
);
}
if your intention is fetching data every time user click submit, based on your useEffect, you should not base your useEffect on search but on items itself (as when user submit, you add something to items)
your code should look like this
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`http://127.0.0.1:5000/api/resources/products?url=${url}`,
)
setItemDetails(result.data[0]);
}
fetchData()
}, [items])
I am using material ui popper and want to separate the transition into a separate function as follows
import React from 'react';
import { makeStyles, Theme, createStyles } from '#material-ui/core/styles';
import Popper from '#material-ui/core/Popper';
import Fade from '#material-ui/core/Fade';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
paper: {
border: '1px solid',
padding: theme.spacing(1),
backgroundColor: theme.palette.background.paper,
},
}),
);
export default function TransitionsPopper() {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const open = Boolean(anchorEl);
const id = open ? 'transitions-popper' : undefined;
const popperTrans = ({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<div className={classes.paper}>The content of the Popper.</div>
</Fade>
)
return (
<div>
<button aria-describedby={id} type="button" onClick={handleClick}>
Toggle Popper
</button>
<Popper id={id} open={open} anchorEl={anchorEl} transition>
{popperTrans}
</Popper>
</div>
);
}
I am using typescript and it's throwing tslint error Binding element 'TransitionProps' implicitly has an 'any' type. How can I type TransitionProps here?
Alternatively, TransitionProps is exported, so you can do:
import { TransitionProps as TransitionPropsType } from "#material-ui/core";
Then:
const popperTrans = ({ TransitionProps }: { TransitionProps: TransitionPropsType }) => (
<Fade {...TransitionProps} timeout={350}>
<div className={classes.paper}>The content of the Popper.</div>
</Fade>
)
I found the answer. We can import FadeProps from Fade and use that to type TransitionProps.
import React from 'react';
import { makeStyles, Theme, createStyles } from '#material-ui/core/styles';
import Popper from '#material-ui/core/Popper';
import Fade, { FadeProps } from '#material-ui/core/Fade';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
paper: {
border: '1px solid',
padding: theme.spacing(1),
backgroundColor: theme.palette.background.paper,
},
}),
);
export default function TransitionsPopper() {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const open = Boolean(anchorEl);
const id = open ? 'transitions-popper' : undefined;
const popperTrans = ({ TransitionProps }: { TransitionProps: FadeProps }) => (
<Fade {...TransitionProps} timeout={350}>
<div className={classes.paper}>The content of the Popper.</div>
</Fade>
)
return (
<div>
<button aria-describedby={id} type="button" onClick={handleClick}>
Toggle Popper
</button>
<Popper id={id} open={open} anchorEl={anchorEl} transition>
{popperTrans}
</Popper>
</div>
);
}
I have a modal that opens onMouseEnter which works great, however, I'm having trouble closing it onMouseLeave when the user stops hovering over the button.
Here is my component:
I've tried adding an event listener, onMouseLeave to the button tag, but it does not work correctly. Any ideas?
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Modal from '#material-ui/core/Modal';
import Backdrop from '#material-ui/core/Backdrop';
import Fade from '#material-ui/core/Fade';
const useStyles = makeStyles(theme => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
const DistributionLineOverflow = props => {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<button type="button" onMouseEnter={handleOpen}>
i
</button>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<div className={classes.paper}>
<p id="transition-modal-description">
Service Dates: {props.serviceDates}
</p>
</div>
</Fade>
</Modal>
</div>
);
};
export default DistributionLineOverflow;
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [open, setOpen] = useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button
onMouseEnter={() => handleOpen()}
onMouseLeave={() => handleClose()}
>
i
</button>
<div
style={{
width: 500,
height: 500,
backgroundColor: "blue",
display: open ? "block" : "none"
}}
/>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Try calling your methods like this instead.
I created a code sandbox with similar behaviour: https://codesandbox.io/s/crazy-meninsky-7fl2o
I updated the codesandbox link, but it seems that codesandbox is having some problem running it for me so I have posted the whole code here as well.