Best way to validate a component passed as a prop - reactjs

What is the best way to validate proptypes that are going to be stateless components , other than Proptypes.func ?
Let's say I have a NestedDashboardItem component that is going to receive an Icon component as a prop . I don't want to pass it as a child and use it from props.children because I have use cases where that'll be overkill like passing an array of child nested Links and such .
consider :
<DashboardItem
to="/route"
icon={someIconComponent}
text="sometext"
/>
and in the DashboardItem definition I want to validate this icon prop
const DashboardItem = ({ text, icon, to, classes }) => {
const Icon = icon;
return (
<ListItem
button
component={Link}
to={to}
activeClassName="active"
className={classes.root}
>
<ListItemIcon className={cx(classes.dashboardIcon_wrapper)}>
<Icon className={cx(classes.icon)} />
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
);
};
I used PropTypes.element and it complained , so now what I'm asking is what is the best way to validate the icon prop , other than PropTypes.func ?

There is no better way. Use PropTypes.func.

Related

Child component updates parent state unexpectedly in react

I'm using react with MUI. I'm using the MUI List component which each list may have a nested list in it. the problem is when I open a nested list, the parent component gets closed. I don't know what I am doing wrong. You could use this CodeSandbox.
Here is an image of my app: Gif
Your problem is you add onClick on ListItem which contains all other sub-items, so that means whenever you trigger click on any sub-item, you accidentally trigger the main ListItem too. That's why you've seen the main menu item gets collapsed when you click on any sub-item. You can read more about event propagation.
The easiest fix is you should move onClick={handleToggleCollapse} from ListItem to ListItemButton
<ListItem {...listItemProps} sx={{ display: "block" }}>
<ListItemButton onClick={handleToggleCollapse}>
{icon && <ListItemIcon>{icon}</ListItemIcon>}
<ListItemText primary={text} />
{open ? (
<IoIosArrowUp className="text-lg" />
) : (
<IoIosArrowDown className="text-lg" />
)}
</ListItemButton>
<Collapse in={open} timeout={300}>
<List disablePadding>
<ListItems listItems={nestedItems} />
</List>
</Collapse>
</ListItem>
For event propagation prevention, you should modify handleToggleCollapse like below
function handleToggleCollapse(e: SyntheticEvent) {
e.stopPropagation();
setOpen((prev) => !prev);
}
Sandbox

Pass icon button as props using react

I have a common grid component where I define the structure of the grid and also structure of a button bar that goes on top of the grid.
Common-grid.js
<Box height='100%' width='100%' position='absolute'>
<div className="common-grid">
<div className="button-bar">
</div>
<div className="ag-grid">
</div>
</div>
</Box>
I pass data to the grid from my other component based to fill in the grid.
MyComponent.js
{gridData.length > 0 && <Grid tableData={gridData} columnData={activeListColumnDef} {...props}/>}
Along with the data, I would also like to pass icon buttons that I would like to see in button bar.
<IconButton
icon={<AddIcon />}
onClick={onClickOpenActiveListEditor}
/>
<IconButton
icon={<EditIcon />}
/>
I do not want to define icon buttons in the common component but pass it as props. Is it possible to pass such html elements along with its event listeners as props? Please help!
Sure, it's called a render prop. Just directly pass the node like this:
// in the parent component
<Grid
tableData={gridData}
columnData={activeListColumnDef}
icon={<AddIcon onClick={onClickOpenActiveListEditor} />}
{...props}
/>
// in the Grid component
function Grid({tableData, columnData, icon}){
return (
<>
// grid stuff
{icon && icon}
</>
)
}
If you need typescript support, the node would typed as such:
interface GridProps{
// stuff
icon?: React.ReactNode;
}
You could do something like:
const renderIcon = (onClick) => {
return <Icon onClick={onClick} />
}
...
<IconButton renderIcon={renderIcon} />
Then, inside IconButton:
{renderIcon()}

Unable to solve the Menu bar issue using reactjs?

I'm new to Framework and i have an issue with me sidebar which is showing Table tab as default but it should not show any tab's page as clicked. By default it should show nothing. When we click on a particular tab then it should show color on the tab of the sidebar. If i give -1 then the color is getting disappeared on the menu tab but the Tab's page remain as it is. Is it possible to give pathname. how to fix by giving id ? . If Yes Can anyone help me in fixing this issue?
Here is the code:
<Drawer
className={classes.drawer}
variant="permanent"
classes={{
paper: classes.drawerPaper
}}
>
<div className={classes.toolbar} />
<List>
{["table", "home"].map((item, index) => {
const Icon = itemsConfig[item].icon;
return (
<ListItem
component={Link}
to={itemsConfig[item].link}
selected={selectedIndex === index}
onClick={event => handleListItemClick(event, index)}
button
key={item}
>
<ListItemIcon>
<Icon />
</ListItemIcon>
<ListItemText primary={itemsConfig[item].text} />
</ListItem>
);
})}
</List>
</Drawer>
Here is whole code: "https://codesandbox.io/s/fervent-browser-ecz7z"
Can anyone help me in this ?
Define your state, that holds the selected index in your <Layout /> component. Define function that manipulate that state and pass it to , <Home /> andcomponents along with the newly defined state. Finally call the function after the initial rendering of the component. For the (class component) use:
componentDidMount() {
this.props.setTabIndex(this.props.index);
}
For the <Table /> use:
useEffect(() => {
props.setTabIndex(props.index);
}, []);
The full result is posted here: https://codesandbox.io/s/cold-mountain-hlhk8?

How to determine which icon is clicked in a menu with Material-UI icons

When clicking an icon in a material-ui menu, event.target does not provide values to determine which icon was clicked
Here is my code:
<ListItem button id="Dashboard" onClick={handleMenuListClick}>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
When clicking on the button text area (ie. "Dashboard"), I am able to read the event.target values to determine which menu listitem was clicked. When clicking on the DashboardIcon, the handler is triggered but the event.target yields the svg (ex. svg class="MuiSvgIcon-root"...
Is there a better way to determine which icon was clicked?
Like I was saying in the comments, I would make an inline lambda or bound function here to do this. Be explicit with your code and pass values that you expect. It'll be less error prone in the long run :) You should define a constant / enum to map to as well.
const Pages = {
dashboard: 'Dashboard',
profile: 'Profile'
}
and then render it
<ListItem
button
id={Pages.dashboard}
onClick={() => handleMenuListClick(Pages.dashboard)}
>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
In Typescript you can define enums 😍
enum Pages {
dashboard = 'dashboard',
profile = 'profile'
}

React implementation of Material Design with presentational components

Is it possible to use Material UI with stateless components, or is state a requirement?
I intended to implement Popovers, and from what I gathered from the official code example is that it's state-depenedent.
It depends on the Material UI component.
Some are ideal, and are recommended, as stateless components. In fact, many of the examples in the Material UI documentation use stateless components. For example, a <Badge /> component:
const BadgeExampleSimple = () => (
<div>
<Badge
badgeContent={4}
primary={true}
>
<NotificationsIcon />
</Badge>
<Badge
badgeContent={10}
secondary={true}
badgeStyle={{top: 12, right: 12}}
>
<IconButton tooltip="Notifications">
<NotificationsIcon />
</IconButton>
</Badge>
</div>
);
Or an <Icon />:
const HomeIcon = (props) => (
<SvgIcon {...props}>
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
</SvgIcon>
);
Other components require them to be stateful to manage state like open.
And this is the case for the <Popover /> component unfortunately.
export default class PopoverExampleSimple extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
};
}
...
}
So, to answer your questions:
Yes, it's possible to use a stateless component if the component does not require state
No, it's not a requirement to use a stateful component, unless the component requires state

Resources