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... />
Related
I am trying to build a hamburger menu using Material UI List on Next.js similar to Nested List example on Material UI docs. However, I am keep getting error if I use "collapse" on my code. If I remove collapse then the other parts of the code works fine.
Can anyone tell me what am I doing wrong?
screenshot of an error message
I am trying to build similar to this
import * as React from "react";
import { useState } from "react";
import IconButton from "#mui/material/IconButton";
import ListSubheader from "#mui/material/ListSubheader";
import { Drawer } from "#mui/material";
import List from "#mui/material/List";
import ListItem from "#mui/material/ListItem";
import ListItemButton from "#mui/material/ListItemButton";
import ListItemIcon from "#mui/material/ListItemIcon";
import ListItemText from "#mui/material/ListItemText";
import MenuIcon from "#mui/icons-material/Menu";
import { ExpandLess, ExpandMore } from "#mui/icons-material";
import Collapse from "#mui/material";
const myMenu = [
{
title: "about",
},
{
title: "services",
submenu: [{ title: "Digital Marketing" }, { title: "Promotion" }],
},
{
title: "blog",
},
{
title: "contact",
},
];
const ResponsiveAppBar = () => {
const [openDrawer, setOpenDrawer] = useState(false);
const [open, setOpen] = React.useState(false);
const handleClick = () => {
setOpen(!open);
};
return (
<React.Fragment>
<Drawer
anchor="right"
open={openDrawer}
onClose={() => setOpenDrawer(false)}
>
<List
sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }}
component="nav"
aria-labelledby="nested-list-subheader"
subheader={
<ListSubheader component="div" id="nested-list-subheader">
Navigation
</ListSubheader>
}
>
{myMenu.map((page, index) =>
page.submenu ? (
<div>
<ListItemButton key={index} onClick={handleClick}>
<ListItemText>{page.title}</ListItemText>
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{page.submenu.map((sub, index) => {
return (
<ListItemButton key={index}>
<ListItem key={index}>
<ListItemText>{sub.title}</ListItemText>
</ListItem>
</ListItemButton>
);
})}
</List>
</Collapse>
</div>
) : (
<ListItemButton key={index}>
<ListItemIcon>
<ListItemText>{page.title}</ListItemText>
</ListItemIcon>
</ListItemButton>
)
)}
</List>
</Drawer>
<IconButton
sx={{ color: "black", marginLeft: "auto" }}
onClick={() => setOpenDrawer(!openDrawer)}
>
<MenuIcon color="white" />
</IconButton>
</React.Fragment>
);
};
export default ResponsiveAppBar;
The culprit is an incorrect import of Collapse:
import Collapse from "#mui/material";
Since Collapse is a named import, you must import it in one of these two ways:
import { Collapse } from "#mui/material";
// Alternatively
import Collapse from "#mui/material/Collapse";
I am trying to get the user.id of the user that I clicked on, so that I can use it as a query parameter in a fetch(). However, what setClickedUserId is passing/assigning to my clickedUserId state is the following: MuiButtonBase-root%20MuiListItem-root%201%20MuiListItem-gutters%20MuiListItem-button
----My code:
import React from 'react';
import {makeStyles} from '#material-ui/core/styles';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import Collapse from '#material-ui/core/Collapse';
import ExpandLess from '#material-ui/icons/ExpandLess';
import ExpandMore from '#material-ui/icons/ExpandMore';
import PersonIcon from '#material-ui/icons/Person';
import AddBoxOutlinedIcon from '#material-ui/icons/AddBoxOutlined';
// Global context
import globalContext from './globalContext';
const useStyles = makeStyles((theme) => ({
root: {
marginTop: theme.spacing(-2.0),
width: '100%',
maxWidth: 360,
backgroundColor: '#3F0E40',
color: 'white',
},
nested: {
marginLeft: theme.spacing(-3.5),
},
nestedAdd: {
marginLeft: theme.spacing(3.5),
},
nestedAddText: {
marginLeft: theme.spacing(0.5),
},
}));
/**
*#return{any} nestedList
*/
export default function DmsList() {
const classes = useStyles();
const [open, setOpen] = React.useState(true);
console.log('I am inside DmsList!!');
// Global Context
const {setMobileOpen} = React.useContext(globalContext);
const {currentDmdWith} = React.useContext(globalContext);
const {setChannel} = React.useContext(globalContext);
const {clickedDms, setClickedDms} = React.useContext(globalContext);
const {setClickedUserId} = React.useContext(globalContext);
const handleClickUser = (event) => {
setMobileOpen(false);
setClickedDms('true');
setChannel(event.target.innerText);
setClickedUserId(event.target.className);
console.log(clickedDms);
};
const handleClickAdd = () => {
setMobileOpen(false);
};
const handleClick = () => {
setOpen(!open);
};
return (
<List
component="nav"
aria-labelledby="nested-list-subheader"
className={classes.root}
>
<ListItem button
onClick={handleClick}>
{open ? <ExpandLess /> : <ExpandMore />}
<ListItemText primary="Direct Messages" />
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding className={classes.nested}>
{currentDmdWith.map((user) => (
<ListItem button className={user.id} onClick={handleClickUser}>
<ListItemIcon className={user.id}>
</ListItemIcon>
<PersonIcon className={user.id} color='white'/>
<ListItemText className={user.id} primary={user.name} />
</ListItem>
))}
<ListItem button
onClick={handleClickAdd}
className={classes.nested}>
<ListItemIcon>
</ListItemIcon>
<AddBoxOutlinedIcon className={classes.nestedAdd}/>
<ListItemText
className={classes.nestedAddText} primary="Add Teammate" />
</ListItem>
</List>
</Collapse>
</List>
);
}
I would use event.target.id, because it actually gets the div's id and not a bunch of mumbo jumbo, but when I do put id = {user.id} in a ListItem I have to click on a very specific spot, the leftmost side, because if I click on an a person icon or the area with text, event.target.id is a assigned a blank and makes my fetch() return a 404 error.
Instead of trying to read the information out of the Event object, pass the info that you need in the handleClickUser function through the onClick prop.
<ListItem
button
className={user.id}
onClick={() => handleClickUser(user.id)}
>
const handleClickUser = (userId) => {
setClickedUserId(userId);
};
I have parent & its 2 child component
Parent code:
import React from 'react';
import { Grid, CircularProgress } from '#mui/material';
import { useSelector } from 'react-redux';
import Angle from './Angle/Angle';
import CustomSnackbar from '../Snackbar/CustomSnackbar';
import useStyles from './styles';
const Angles = ({ setCurrentId }) => {
const angles = useSelector((state) => state.angles);
const [snackbarOpenProp, setSnackbarOpenProp] = React.useState(false);
const classes = useStyles();
const handleSnackBarCloseAction = () => {
setSnackbarOpenProp(false)
}
const handleAddToCartAction = () => {
console.log('click');
setSnackbarOpenProp(true)
}
return (
!angles.length ? <CircularProgress /> : (
<Grid className={classes.container} container alignItems="stretch" spacing={3}>
{angles.map((angle) => (
<Grid key={angle._id} item xs={12} sm={6} md={6}>
<Angle angle={angle} setCurrentId={setCurrentId} handleAddToCart={handleAddToCartAction}/>
</Grid>
))}
<CustomSnackbar openState={snackbarOpenProp} handleSnackBarCloseProp={handleSnackBarCloseAction}/>
</Grid>
)
);
};
export default Angles;
Child 1 code:
import React from 'react';
import { Card, CardActions, CardContent, CardMedia, Button, Typography } from '#mui/material';
import Add from '#mui/icons-material/Add';
import { useDispatch } from 'react-redux';
import useStyles from './styles';
const Angle = ({ angle, setCurrentId,handleAddToCart }) => {
const dispatch = useDispatch();
const classes = useStyles();
return (
<Card className={classes.card}>
<CardMedia className={classes.media} image={angle.image} />
<div className={classes.overlay}>
<Typography variant="h6">{angle.qualityName}</Typography>
<Typography variant="body2">{angle.colors}</Typography>
</div>
<div className={classes.overlay2}>
<Button onClick={handleAddToCart} style={{ color: 'white' }} size="small" onClick={() => setCurrentId(angle._id)}><Add fontSize="large" /></Button>
</div>
</Card>
);
};
export default Angle;
Child 2 code:
import * as React from 'react';
import Button from '#mui/material/Button';
import Snackbar from '#mui/material/Snackbar';
import IconButton from '#mui/material/IconButton';
import CloseIcon from '#mui/icons-material/Close';
export default function CustomSnackbar(props) {
const action = (
<React.Fragment>
<Button color="secondary" size="small" onClick={props.handleSnackBarCloseProp}>
UNDO
</Button>
<IconButton
size="small"
aria-label="close"
color="inherit"
onClick={props.handleSnackBarCloseProp}
>
<CloseIcon fontSize="small" />
</IconButton>
</React.Fragment>
);
return (
<div>
<Snackbar
open={props.openState}
autoHideDuration={6000}
onClose={props.handleSnackBarCloseProp}
message="Note archived"
action={action}
/>
</div>
);
}
As you can see, I am trying to attach the onClick event which is in Child1 Angle code & based on its click I am trying to change a state value of another child2 CustomSnackbar and sending it as a prop, but on clicking I am not getting a response, How can I do it, also is there any more simple way to achieve this ?
There are two onClick methods on the button component. Please change according below example and try it.
Before:
<Button onClick={handleAddToCart} style={{ color: 'white' }} size="small" onClick={() => setCurrentId(angle._id)}><Add fontSize="large" /></Button>
After:
<Button
style={{ color: 'white' }}
size="small"
onClick={() => {
handleAddToCart();
setCurrentId(angle._id)
}
}>
<Add fontSize="large" />
</Button>
I am trying to create a custom hook which returns a custom drawer and a button(to toggle the state of drawer). I am managing the state in custom hook itself.
This is my custom hook for returning the drawer and a button
import React from "react";
import clsx from "clsx";
import { makeStyles } from "#material-ui/core/styles";
import Drawer from "#material-ui/core/Drawer";
import List from "#material-ui/core/List";
import Divider from "#material-ui/core/Divider";
import ListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import ListItemText from "#material-ui/core/ListItemText";
import InboxIcon from "#material-ui/icons/MoveToInbox";
import MailIcon from "#material-ui/icons/Mail";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faBars } from "#fortawesome/free-solid-svg-icons";
import { Button } from "#material-ui/core";
const useStyles = makeStyles({
list: {
width: 250
},
fullList: {
width: "auto"
}
});
export default function useTemporaryDrawer(toggle) {
const classes = useStyles();
const [state, setState] = React.useState({
right: false
});
const toggleDrawer = (anchor, open) => (event) => {
if (
event.type === "keydown" &&
(event.key === "Tab" || event.key === "Shift")
) {
return;
}
alert("setting");
setState({ ...state, [anchor]: open });
};
const onButtonClick = (e) => {
toggleDrawer("right", true)(e);
};
const Toggler = (
<Button
style={{
marginTop: "10px"
}}
onClick={onButtonClick}
startIcon={<FontAwesomeIcon icon={faBars} style={{ color: "#fff" }} />}
></Button>
);
const list = (anchor) => (
<div
className={clsx(classes.list, {
[classes.fullList]: anchor === "top" || anchor === "bottom"
})}
role="presentation"
onClick={toggleDrawer(anchor, false)}
onKeyDown={toggleDrawer(anchor, false)}
>
<List>
{["Inbox", "Starred", "Send email", "Drafts"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{["All mail", "Trash", "Spam"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</div>
);
const CustomDrawer = (
<Drawer
anchor={"right"}
open={state["right"]}
onClose={toggleDrawer("right", false)}
>
{list("right")}
</Drawer>
);
return {
CustomDrawer,
setState,
state,
Toggler
};
}
Then, I am trying to update the state through Toggle button which I am using in Header. This is my header
import React from "react";
import styles from "./Header.module.css";
import useTemporaryDrawer from "../Drawer/useTemporaryDrawer";
const Header = (props) => {
const { Toggler } = useTemporaryDrawer();
return <div className={styles.headerForApp}>{Toggler}</div>;
};
export default Header;
When I press the toggle button, it does update the state but it does not update the open property of drawer. In short I am not able to open the drawer through toggle button. Please help.
Here is the sandbox
https://codesandbox.io/s/customhooksnotupdatingstate-d7mfz?file=/src/components/Drawer/useTemporaryDrawer.js
If I have 2 components: A and B, and each of them have setState hook to update variable state, you won't expect setState('a') inside A to make any difference to state in B, will you? But then you create 2 components, each of them with their independent state and expect those states to be shared somehow. Toggle in Header changes state of Header, and Drawer in Dashboard had no visibility of these changes. As an option you can pass Toggler to the Header as a prop
<Header Toggler={Toggler} />
const Header = (props) => {
return <div className={styles.headerForApp}>{props.Toggler}</div>;
};
I have built a Trello clone using ReactJS, where I have 4 columns called TODO, DOING, DONE and REJECTED, where I can add a card to any column.
In a file I am trying to map over card component and rendering properties from defined dummy data.
What I want to do?
I want to delete a specific card when the button is clicked.
What I tried?
I have added the functionality in my Redux store, but when adding the onclick event to my button, I cannot access the dispatch method which will trigger the deleteCard function.
How do I do that?
My TaskboardList.js component :
import React from "react";
import TaskboardCard from "./TaskboardCard";
import TaskboardActionButton from "./TaskboardActionButton";
import { Droppable } from "react-beautiful-dnd";
const TaskboardList = ({ title, cards, listID }) => {
return (
<Droppable droppableId={String(listID)}>
{provided => (
<div
className="taskboardlist_container"
{...provided.droppableProps}
ref={provided.innerRef}
style={styles.container}
>
<div className="sub-heading">{title}</div>
{cards.map((card, index) => (
<TaskboardCard
key={card.id}
index={index}
text={card.text}
id={card.id}
/>
))}
<TaskboardActionButton listID={listID} />
{provided.placeholder}
</div>
)}
</Droppable>
);
};
const styles = {
container: {
backgroundColor: "#eee",
width: 300,
padding: "0.5rem",
marginRight: "1rem",
height: "100%"
}
};
export default TaskboardList;
My TaskboardCard.js component
import React from "react";
import Card from "#material-ui/core/Card";
import Typography from "#material-ui/core/Typography";
import CardContent from "#material-ui/core/CardContent";
import { Draggable } from "react-beautiful-dnd";
import { connect } from "react-redux";
import { deleteCard } from "../actions";
const TaskboardCard = ({ text, id, index, sample, cardId }) => {
// handleClickDelete = () => {
// // const { dispatch } = this.props;
// // dispatch(deleteCard(cardId));
// console.log("clicked");
// };
return (
<Draggable draggableId={String(id)} index={index}>
{provided => (
<div
className="taskboard_container"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card>
<CardContent>
<Typography style={{ fontSize: "1.5rem" }} gutterBottom>
{text}
</Typography>
</CardContent>
</Card>
{/* //delete added */}
<button
onClick={(cardId, props, sample, dispatch) => {
//const { dispatch } = this.props;
dispatch(deleteCard(cardId));
}}
>
DELETE
</button>
{/* ////////////////////// */}
</div>
)}
</Draggable>
);
};
export default connect()(TaskboardCard);
In the above component delete button is not working because somehow i cannot access the dispatch.
Here is my codesandbox link for further reference to files https://codesandbox.io/s/github/abhinav-anshul/consensolabs
Remove the props and dispatch from onclick event and add dispatch in component parameter list.
If you don't specify the second argument to connect(), your component will receive dispatch by default in porps.
import React from "react";
import Card from "#material-ui/core/Card";
import Typography from "#material-ui/core/Typography";
import CardContent from "#material-ui/core/CardContent";
import { Draggable } from "react-beautiful-dnd";
import { connect } from "react-redux";
import { deleteCard } from "../actions";
const TaskboardCard = ({ text, id, index, sample, cardId, dispatch }) => {
// handleClickDelete = () => {
// // const { dispatch } = this.props;
// // dispatch(deleteCard(cardId));
// console.log("clicked");
// };
return (
<Draggable draggableId={String(id)} index={index}>
{provided => (
<div
className="taskboard_container"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card>
<CardContent>
<Typography style={{ fontSize: "1.5rem" }} gutterBottom>
{text}
</Typography>
</CardContent>
</Card>
{/* //delete added */}
<button
onClick={(cardId, sample) => {
//const { dispatch } = this.props;
dispatch(deleteCard(cardId));
}}
>
DELETE
</button>
{/* ////////////////////// */}
</div>
)}
</Draggable>
);
};
export default connect()(TaskboardCard);