I want to prevent that when I click on several items, I have several items (expand: "true")
I would like that when I click on a new item, the old one goes back to its original state (expand: "false")
However, I did not find any property in the documentation to handle this.
https://material-ui.com/components/tree-view/
{stoMenu && (
<TreeView
style={layout.menu}
defaultCollapseIcon={<KeyboardArrowUpIcon />}
defaultExpandIcon={<KeyboardArrowDownIcon />}
>
{stoMenu.root.children.map(menu => {
return (
<TreeItem
key={menu.nodeId}
nodeId={menu.nodeId}
label={
<ListItem
style={layout.menuListItem}
className={menu.iconCls}
>
<ListItemText
style={layout.menuText}
primary={menu.text}
onClick={() => {
if (menu.id === '/accueil') {
this.props.history.push(menu.id);
}
}}
/>
</ListItem>
}
>
{menu.children.map(child => {
return (
<TreeItem
// style={layout.subMenuText}
className={classes.subMenu}
key={child.nodeId}
nodeId={child.nodeId}
label={child.text}
onClick={() => {
if (child.id) {
this.props.history.push(child.id);
}
}}
/>
);
})}
</TreeItem>
);
})}
</TreeView>
)}
Related
I'm designing the frontend of a chat application and this is the Conversations component.
The problem is on line 87 and I've highlighted the line. 'conversations' is an array so I'm unable to figure out the issue here. I'm actually quite new to react so apologies if it's something silly. Thanks!
const Conversations = (props) => {
const classes = useStyles();
const [conversations, setConversations] = useState([]);
const [newConversation, setNewConversation] = useState(null);
const getConversations = useGetConversations();
useEffect(() => {
getConversations().then((res) => setConversations(res));
}, [newConversation]);
useEffect(() => {
let socket = socketIOClient(process.env.REACT_APP_API_URL);
socket.on("messages", (data) => setNewConversation(data));
return () => {
socket.removeListener("messages");
};
}, []);
return (
<List className={classes.list}>
<ListItem
classes={{ root: classes.subheader }}
onClick={() => {
props.setScope("Global Chat");
}}
>
<ListItemAvatar>
<Avatar className={classes.globe}>
<LanguageIcon />
</Avatar>
</ListItemAvatar>
<ListItemText className={classes.subheaderText} primary="Global Chat" />
</ListItem>
<Divider />
{conversations && (
<React.Fragment>
// 87 {conversations.map((c) => ( // error on this line
<ListItem
className={classes.listItem}
key={c._id}
button
onClick={() => {
props.setUser(handleRecipient(c.recipientObj));
props.setScope(handleRecipient(c.recipientObj).name);
}}
>
<ListItemAvatar>
<Avatar>
{commonUtilites.getInitialsFromName(
handleRecipient(c.recipientObj).name
)}
</Avatar>
</ListItemAvatar>
<ListItemText
primary={handleRecipient(c.recipientObj).name}
secondary={<React.Fragment>{c.lastMessage}</React.Fragment>}
/>
</ListItem>
))}
</React.Fragment>
)}
</List>
);
};```
I think you need to set data which you are getting in getConversations.
setConversations(res.data)
This is my scenario
<List>
{mainTags.map((mainTagItem) => {
return (
<ListItem onClick={() => { setMainTag(mainTagItem.tag.tagId) }} button className={classes.mainTagItem}>
<div className={classes.mainTagCircle}></div>
<ListItemText
primary={mainTagItem.tag.name}
/>
</ListItem>
)
})}
</List>
when i click on my ListItem ( that becomes selected ) i want the element <div className={classes.mainTagCircle}> has an active class
For Example:
<div classes={{ root: !!listItemSelected ? classes.mainTagCircleActive : classes.mainTagCircle, }}></div>
I have already a method onClick in my ListItem, how can i apply this logic?
given that you have a mainTag state you could compare with your element tagId to define which class to select. If it's the same as your state then active class wil be returned:
<div className={
mainTag === mainTagItem.tag.tagId
? classes.mainTagCircleActive
: classes.mainTagCircle}>
</div>
Solution with a library
You could try with this library clsx. Then do something like this:
function Component() {
const [mainTag, setMainTag] = useState(null);
return (
<List>
{mainTags.map((mainTagItem) => {
return (
<ListItem onClick={() => { setMainTag(mainTagItem.tag.tagId) }} className=
{clsx([classes.mainTagItem, mainTag === mainTagItem.tag.tagId ? :
'activeClass': 'defaultClass' ])}>
<div className={classes.mainTagCircle}></div>
<ListItemText
primary={mainTagItem.tag.name}
/>
</ListItem>
)
})}
</List>
)
}
Solution without libraries
function Component() {
const [mainTag, setMainTag] = useState(null);
return (
<List>
{mainTags.map((mainTagItem) => {
return (
<ListItem onClick={() => { setMainTag(mainTagItem.tag.tagId) }} className={
mainTag === mainTagItem.tag.tagId
? classes.mainTagCircleActive
: classes.mainTagCircle}
/>
</ListItem>
)
})}
</List>
)
}
Now there is clickable only a table cell containing an icon, like here at the end:
<Table.Body>
{rows.map((row, rowIndex) => (
<Table.Row key={idList && idList[rowIndex]}>
{emptyFirstHeader && (
<Table.Cell>
<Image
src={row.cells[0]}
/>
</Table.Cell>
)}
{
(emptyFirstHeader ? row.cells.shift() : row,
row.cells.map((cell, cellIndex) => {
if (cell === undefined) {
return null;
}
return (
<Table.Cell key={idList}>
{cell}
</Table.Cell>
);
}))
}
<Table.Cell>
<Link //here is the link done - inside a table cell
to={
entityName &&
`/${entityName}/${idList[rows.indexOf(row)]}`
}>
<Icon name="ellipsis horizontal" />
</Link>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
My goal is to make the whole row clickable (and redirect to that new link). So I've tried to make the row clickable:
<Table.Body>
{rows.map((row, rowIndex) => (
<Table.Row key={idList && idList[rowIndex]}>
{emptyFirstHeader && (
<Table.Cell>
<Image
src={row.cells[0]}
/>
</Table.Cell>
)}
{
(emptyFirstHeader ? row.cells.shift() : row,
row.cells.map((cell, cellIndex) => {
if (cell === undefined) {
return null;
}
return (
<Table.Cell key={idList}>
{cell}
</Table.Cell>
);
}))
}
<Table.Cell>
<Icon name="ellipsis horizontal" />
</Table.Cell>
// moved it outside the Tabel.Cell and now it is inside Table.Row
<Link to={entityName && `/${entityName}/${idList[rows.indexOf(row)]}`}>
</Link>
</Table.Row>
))}
</Table.Body>
But unfortunately it's not working. Is there a way to change it and make the whole row clickable?
Put your <Table.Row> element inside the <Link> element. Doing so will "wrap a <Link> behavior around the <Table.Row>".
I believe this code will solve your problem:
<Table.Body>
{rows.map((row, rowIndex) => (
<Link to={entityName && `/${entityName}/${idList[rows.indexOf(row)]}`}>
<Table.Row key={idList && idList[rowIndex]}>
{emptyFirstHeader && (
<Table.Cell>
<Image src={row.cells[0]} />
</Table.Cell>
)}
{
(emptyFirstHeader ? row.cells.shift() : row, row.cells.map((cell, cellIndex) => {
if (cell === undefined) {
return null;
}
return (
<Table.Cell key={idList}>
{cell}
</Table.Cell>
);
}))
}
<Table.Cell>
<Icon name="ellipsis horizontal" />
</Table.Cell>
</Table.Row>
</Link>
))}
</Table.Body>
What's wrong with the following:
<List>
{sections.map(section => (
<>
{section.header && <ListSubheader key={section.header}>{section.header}</ListSubheader>}
{section.items
.filter(item => new RegExp(itemsFilter, 'i').test(item.value))
.map(item => {
const labelId = `multi-select-filter-list-checkbox-label-${item.key}`;
return (
<ListItem key={item.key} role={undefined} dense button onClick={handleToggle(item)}>
<ListItemIcon>
<Checkbox
className={checkboxClasses.root}
edge="start"
checked={checked.indexOf(item) !== -1}
tabIndex={-1}
disableRipple
color="primary"
inputProps={{ 'aria-labelledby': labelId }}
/>
</ListItemIcon>
<ListItemText
id={labelId}
primary={item.value}
primaryTypographyProps={{
variant: 'body1'
}}
/>
</ListItem>
);
})
}
</>
))}
</List>
I believe I'm providing the keys; where is the error?
The key prop should be provided for the wrapping tag.
Replace <> with <React.Fragment> and apply a key to it.
<React.Fragment key={section.header}>
As kind-user user stated you should add key to the wrapping tag.
Key is the only attribute that can be passed to .
Note: When you are passing a key to <>, you can't use this shorthand.
Rather use
<React.Fragment key={yourKey}>
...
<React.Fragment>
Really stumped. I am trying to create a ListItem for every key of every value in an array of objects. When I log item, it returns the key I'm looking for as a string. Great! However, the list items never render on the page.
return (
<div>
<List className="list">
{Object.values(props.sectionInfo).forEach(section => {
Object.keys(section).map((item, index) => {
return (
<ListItem button className='list-item'> //doesn't render, but also doesn't throw errors
<ListItemText primary={item} />
</ListItem>
)
});
})}
</List>
</div>
);
console.log(item) //returns "red", "blue"
The below renders the list perfectly, however the list items are the indexes (0, 1)
return (
<div>
<List className="list">
{Object.keys(props.sectionInfo).map((section, index) => {
return (
<ListItem button className='list-item'>
<ListItemText primary={section} />
</ListItem>
)
})}
</List>
</div>
);
Any insight would be helpful.
This is because you are using the forEach in the outer loop and it doesn't return anything actually, so the children prop of the List is undefined. Try this:
return (
<div>
<List className="list">
{Object.values(props.sectionInfo).map(section => {
return Object.keys(section).map((item, index) => {
return (
<ListItem button className='list-item'>
<ListItemText primary={item} />
</ListItem>
)
});
})}
</List>
</div>
);
Please try to build list of virtual doms as following:
let items = []
Object.values(props.sectionInfo).forEach(section => {
let subItems = Object.keys(section).map((item, index) => {
return (
<ListItem button className='list-item'> //doesn't render, but also doesn't throw errors
<ListItemText primary={item} />
</ListItem>
)
});
items = items.concat(subItems);
})
return (
<div>
<List className="list">
{items}
</List>
</div>
);
Have you tried going through Object.values(section) in the second loop?
Because from your second statement it seems like the contents are indexed as an Array. Maybe you can give more information about the data structure to help you further.