I have implemented Material-UI Slide in my project recently and wanted to ask if someone could explain to me why the code works when I write it this way:
{selectedItem && selectedItem.modal && selectedItem.modal.body ? (
selectedItem.modal.body.map((section, key) => (
<Section section={section} key={key} />
))
) : (
<Slide
direction={animate === 'stepIn' ? 'right' : 'left'}
in={animate === 'idle'}
>
<Grid container={true} spacing={3}>
{items.map((item, key) => (
<Grid item={true} xs={6} md={4} lg={3} key={key}>
<MaterialCard
key={key}
onClick={onCardClicked(item)}
className={classes.card}
>
<CardActionArea className={classes.cardArea}>
<CardMedia
image={item.image || undefined}
component="img"
/>
<CardContent className={classes.cardContent}>
<Typography
component="p"
className={classes.cardContentTypographyHeader}
>
<Hyphenated language={de}>{item.label}</Hyphenated>
</Typography>
{item.description ? (
<Typography
component="p"
className={classes.cardContentTypography}
>
<Hyphenated language={de}>
{item.description}
</Hyphenated>
</Typography>
) : null}
</CardContent>
</CardActionArea>
{selectedItem && selectedItem.id === item.id ? (
<>
<div className={classes.cardSelectedOverlay} />
<Done className={classes.cardSelectedOverlayIcon} />
</>
) : null}
</MaterialCard>
</Grid>
))}
</Grid>
</Slide>
But fails to compile when I move the section.map inside the slide. I want to animate the section coming in aswell.
<Slide
direction={animate === 'stepIn' ? 'right' : 'left'}
in={animate === 'idle'}
>
{selectedItem && selectedItem.modal && selectedItem.modal.body ? (
selectedItem.modal.body.map((section, key) => (
<Section section={section} key={key} />
))
) : (
<Grid container={true} spacing={3}>
{items.map((item, key) => (
<Grid item={true} xs={6} md={4} lg={3} key={key}>
<MaterialCard
key={key}
onClick={onCardClicked(item)}
className={classes.card}
>
<CardActionArea className={classes.cardArea}>
<CardMedia
image={item.image || undefined}
component="img"
/>
<CardContent className={classes.cardContent}>
<Typography
component="p"
className={classes.cardContentTypographyHeader}
>
<Hyphenated language={de}>{item.label}</Hyphenated>
</Typography>
{item.description ? (
<Typography
component="p"
className={classes.cardContentTypography}
>
<Hyphenated language={de}>
{item.description}
</Hyphenated>
</Typography>
) : null}
</CardContent>
</CardActionArea>
{selectedItem && selectedItem.id === item.id ? (
<>
<div className={classes.cardSelectedOverlay} />
<Done className={classes.cardSelectedOverlayIcon} />
</>
) : null}
</MaterialCard>
</Grid>
))}
</Grid>
)}
</Slide>
The code works for every card, except the ones with a modal inside them. A modal contains e.g. text, textinput. When I click on a card with a modal inside of it i get this error:
Thanks for your help!
In the documentation for Slide's children prop you can find:
A single child content element.
⚠️ Needs to be able to hold a ref.
Slide uses React.cloneElement to add a ref and props to the single child. If there are multiple children or if children is an array (even if the array contains only one child), then Slide will get the error you encountered because children.props is not defined and it is trying to reference children.props.style.
Below is a little example to just help better understand the cause of the error:
import React from "react";
import ReactDOM from "react-dom";
const MockSlide = ({ children }) => {
if (children.props) {
return (
<div>
{children}
children.props is defined
</div>
);
}
return (
<div>
{children}
children.props is not defined
</div>
);
};
const sectionArray = [
"An array also causes problems (even if only one element)"
];
function App() {
return (
<div className="App">
<MockSlide>
<div>Single child works fine</div>
</MockSlide>
<br />
<br />
<MockSlide>
<div>Multiple children</div>
<div>causes problems with Slide</div>
</MockSlide>
<br />
<br />
<MockSlide>
{sectionArray.map(section => {
return <div>{section}</div>;
})}
</MockSlide>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
One potential solution is to wrap everything inside the Slide in a single <div> element.
Related
I'm getting the warning message validateDOMNesting(...): cannot descendant of li
in the console and it shows the error is on the treeview and the timeline I dont know where to fix the code, the below code is facing that warning is there any solution for this?
<Box>
<TreeView >
<Timeline key={comment.id}>
<TimelineItem>
<TimelineSeparator>
<Avatar {...stringAvatar(comment.username) } />{" "}
<TimelineConnector />
</TimelineSeparator>
<TimelineOppositeContent style={{maxWidth: "1px",paddingLeft: "0px",paddingRight: "0px", }}/>
<TimelineContent>
<Stack direction="row" spacing={1}>
<Typography variant="h6" sx={{fontSize:"15px" ,fontWeight:"bold"}}>
{comment.username.charAt(0).toUpperCase() + comment.username.slice(1)}
</Typography>
<Item variant="span" sx={{fontSize:"10px"}}> {createdAt}</Item>
</Stack>
<ReactMarkdown children={comment.body} rehypePlugins={[rehypeRaw]} />
<TreeItem
nodeId="1" label={
<TimelineContent>
{canReply && (<Chip label="Reply" onClick={() => setActiveComment({ id: comment.id, type: "replying", }) }
sx={{fontSize:"10px",mb:1}} size="small" icon={<ReplyIcon /> }/> )}
{isReplying && (
<CommentForm submitLabel="Reply" hasCancelButton handleSubmit={(text) => addComment(text, replyId)} handleCancel={() => { setActiveComment(null);}} />)}
</TimelineContent> } >
<TreeItem
nodeId="2"
label={
<Timeline>
{replies.length > 0 && (
<div>
{replies.map((reply) => (
<Comment
comment={reply}
key={reply.id}
setActiveComment={setActiveComment}
activeComment={activeComment}
addComment={addComment}
parentId={comment.id}
replies={[]}
currentUserId={currentUserId}
/>
))}
</div>
)}
</Timeline>}/>
</TreeItem>
</TimelineContent>
</TimelineItem>
</Timeline>
</TreeView>
</Box>
When I try to select a specific button rendered from a 2d array, it ends up selecting all rendered buttons. Is there a way to render the lower details for one specific button.
<Grid container>
{this.serviceByLangauge.map((cat, id) => (
<Grid
item
xs={4}
key={cat.id}
className={this.props.classes.categoryGroup}
>
<Button
variant="contained"
size="medium"
color="primary"
className={this.props.classes.services}
onClick={this.showServices.bind(this, cat.services)}
key={cat.id}
>
{cat.name}
</Button>
<div className={this.props.classes.serviceGroup}>
{servicesList.length > 0 ? (
<List>
{servicesList.map((serv, id) => (
<ListItem key={id}>
<ListItemText primary={id + 1 + ") " + serv.name} />
</ListItem>
))}
</List>
) : (
<div></div>
)}
</div>
</Grid>
))}
</Grid>
]2
can you show us the data of the 2d array?
edit:
I guess you're not iterating servicesList.
Try this instead:
<Grid container>
{this.serviceByLangauge.map((cat, id) => (
<Grid
item
xs={4}
key={cat.id}
className={this.props.classes.categoryGroup}
>
<Button
variant="contained"
size="medium"
color="primary"
className={this.props.classes.services}
onClick={this.showServices.bind(this, cat.services)}
key={cat.id}
>
{cat.name}
</Button>
<div className={this.props.classes.serviceGroup}>
{cat.services.length > 0 ? (
<List>
{cat.services.map((serv, id) => (
<ListItem key={id}>
<ListItemText primary={id + 1 + ") " + serv.name} />
</ListItem>
))}
</List>
) : (
<div></div>
)}
</div>
</Grid>
))}
</Grid>
From the images and the source code you provided, I think to following is wrong:
You derive the services to show from your state. In your state, you have only one list of services. When clicking a button, you update the state with the services of the button.
<Button
variant="contained"
size="medium"
color="primary"
className={this.props.classes.services}
onClick={this.showServices.bind(this, cat.services)}
key={cat.id}
>
Then you iterate on this same state for every button:
<div className={this.props.classes.serviceGroup}>
{servicesList.length > 0 ? (
<List>
{servicesList.map((serv, id) => (
<ListItem key={id}>
<ListItemText primary={id + 1 + ") " + serv.name} />
</ListItem>
))}
</List>
) : (
<div></div>
)}
</div>
I think you want to iterate over cat instead of serviceList:
<div className={this.props.classes.serviceGroup}>
{cat.length > 0 ? (
<List>
{cat.map((serv, id) => (
<ListItem key={id}>
<ListItemText primary={id + 1 + ") " + serv.name} />
</ListItem>
))}
</List>
) : (
<div></div>
)}
</div>
I had to add a condition to make sure that I'm only rendering the div when category id's match
if (this.state.showThis) {
servicesComponent = (
<List className={this.props.classes.serviceGroup}>
{this.state.services.map((serv, id) => (
<ListItem key={id}>
<ListItemText primary={id + 1 + ") " + serv[langCode]} />
</ListItem>
))}
</List>
);
} else {
servicesComponent = <div></div>;
}
const categories = servicesAndCategories.map((category, index) => (
<Grid
item
xs={4}
className={this.props.classes.categoryGroup}
key={index}
>
<Button
variant="contained"
size="medium"
color="primary"
className={this.props.classes.services}
onClick={this.showServices.bind(this, category.services, category.id)}
>
{category[langCode]}
</Button>
{this.state.currentCategory === category.id && servicesComponent}
</Grid>
));
``
Every time I place this Ratings Component in my code it breaks my site. But I need the system can someone help me.
When I use the rating imported from StarRating/> it breaks my site and shows this error message. package link
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of StarRating.
Code:
export default function FullWidthTabs(props) {
const classes = useStyles();
const theme = useTheme();
const [value, setValue] = React.useState(0);
function handleChange(event, newValue) {
setValue(newValue);
}
function handleChangeIndex(index) {
setValue(index);
}
return (
<div className={classes.root}>
<AppBar position="static" color="default">
<Tabs
value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
variant="fullWidth"
aria-label="full width tabs example"
>
<Tab
label={props.product.reviews ? 'Reviews' : 'No Reviews'}
{...a11yProps(0)}
/>
<Tab label="Write A Review" {...a11yProps(1)} />
</Tabs>
</AppBar>
<SwipeableViews
axis={theme.direction === 'rtl' ? 'x-reverse' : 'x'}
index={value}
onChangeIndex={handleChangeIndex}
>
<TabPanel value={value} index={0} dir={theme.direction}>
{props.product.reviews.map((review, i) => {
return (
<div key={i}>
<ListItem button>
<ListItemAvatar>
<Avatar className={classes.orangeAvatar}>
{review.name.charAt(0)}
</Avatar>
</ListItemAvatar>
<ListItemText>
<div className="text-center">
<small>{review.date}</small>
</div>
<div className="text-center">
{<i className={`fas fa-flag`}> {review.country}</i>}
</div>
<div id="reviewName">
<span>{review.name} </span>
<br />
<small style={{ color: 'green' }}>Verified Buyer</small>
<br />
</div>
<b>{review.title}</b>
<br />
{review.reviewMessage}
</ListItemText>
</ListItem>
** <StarRating /> **
</div>
);
})}
</TabPanel>
<TabPanel value={value} index={1} dir={theme.direction}>
Item Two
</TabPanel>
</SwipeableViews>
</div>
);
}
This is the library im importing into StarRating
import React from 'react';
var Rating = require('react-rating');
const StarRating = () => {
return (
<div>
<Rating />
</div>
);
};
export default StarRating;
When I use a few icon menus than box-shadow looks very dark. How to fix that?
1:
Codesandbox example https://codesandbox.io/embed/flamboyant-tdd-r83u1
<div>
{items.map((item, index) => {
return (
<Fragment key={index}>
<IconButton
aria-owns={open ? "long-menu" : undefined}
onClick={this.handleClick}
>
<MoreVertIcon />
</IconButton>
<Menu anchorEl={anchorEl} open={open} onClose={this.handleClose}>
{options.map(option => (
<MenuItem key={option} onClick={this.handleClose}>
{option}
</MenuItem>
))}
</Menu>
</Fragment>
);
})}
</div>
Because, actually you are triggering multiple menus with the same flag at the same time. So shadow is dark because there are multiple menus one after the other.
Below code should fix this, You don't have to render Menu in items loop
render() {
const items = [...Array(10).keys()];
const { anchorEl } = this.state;
const open = Boolean(anchorEl);
return (
<div>
{items.map((item, index) => {
return (
<Fragment key={index}>
<IconButton
aria-owns={open ? "long-menu" : undefined}
onClick={this.handleClick}
>
<MoreVertIcon />
</IconButton>
</Fragment>
);
})}
<Menu anchorEl={anchorEl} open={open} onClose={this.handleClose}>
{options.map(option => (
<MenuItem key={option} onClick={this.handleClose}>
{option}
</MenuItem>
))}
</Menu>
</div>
);
}
I am using a table example from material ui.
This is the link to the online project https://codesandbox.io/s/209kpmpvx0
The example allows you to cell multiple rows and a delete icon gets displayed when you click on the row.
I would like to be able to print out all the selected rows after the delete icon has been pressed.
This is the class that allows you to select a row.
class EnhancedTable extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: [],
}
handleClick = (event, id) => {
const { selected } = this.state;
const selectedIndex = selected.indexOf(id);
let newSelected = [];
this.setState({ selected: newSelected });
};
I put the following code selectedId={selected.id} into the EnhancedTableHead component.
return (
<Paper className={classes.root}>
<EnhancedTableToolbar numSelected={selected.length} />
<div className={classes.tableWrapper}>
<Table className={classes.table} aria-labelledby="tableTitle">
<EnhancedTableHead
numSelected={selected.length}
selectedId={selected.id}
order={order}
orderBy={orderBy}
onSelectAllClick={this.handleSelectAllClick}
onRequestSort={this.handleRequestSort}
rowCount={data.length}
/>
Then I added the selectedId to the EnhancedTableToolbar. I also added an event handler into the delete icon.
let EnhancedTableToolbar = props => {
const { numSelected, classes } = props;
return (
<Toolbar
className={classNames(classes.root, {
[classes.highlight]: numSelected > 0
})}
>
<div className={classes.title}>
{numSelected > 0 ? (
<Typography color="inherit" variant="subheading">
{numSelected} selected
</Typography>
) : (
<Typography variant="title" id="tableTitle">
Nutrition
</Typography>
)}
</div>
<div className={classes.spacer} />
<div className={classes.actions}>
{numSelected > 0 ? (
<Tooltip title="Delete">
<IconButton aria-label="Delete">
<DeleteIcon onClick={printdeletearray} />
</IconButton>
</Tooltip>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="Filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)}
</div>
</Toolbar>
);
};
I get an error saying selectedId is undefined.
const printdeletearray = () => {
console.log(this.state.selected);
};
You made two mistakes I guess:
firstly you should pass selected as selectedId props in <EnhancedTableToolbar />:
<EnhancedTableToolbar
numSelected={selected.length}
selectedId={selected}
/>
the second mistake is that you should pass selectedId to printdeletearray() like this:
<DeleteIcon onClick={() => printdeletearray(selectedId)} />
and here is your Demo