how to handle event bubling in React - javascript

I want to create an onBlur event on the left panel such that it automatically closes when the user clicks the panel to the right. I tried using the onMouseLeave event but the closing animation is not smooth. I also wanted if they could click a button on the right panel it closes the left panel at the same time execute the button function which rules off the use of a click away listener.
all this works with the on blur but the problem is how do I stop the event from firing when I click its child component
left panel code
const options = [
{
title: 'System Config',
icon: Settings,
list: [
{ name: 'General Settings', to: path },
{ name: 'Product Defaults', to: `${url}/pDefaults` },
{ name: 'Inventory Impects', to: `${url}/invImpects` },
],
},{
title: 'dashboard',
icon: Dashboard,
list: [{ name: 'Reports', to: `${url}/reports` }],
},
];
<Drawer onBlur={() => setOpen(false)}>
<Box>
<IconButton onClick={handleDrawer}>
{!open ? <ChevronRightIcon /> : <ChevronLeftIcon />}
</IconButton>
<Divider />
</Box>
<List>{options.map((opt, i) =>(
<ListItem button key={opt}>
<NavLink activeClassName={classes.activeTab} to={opt.to}>
<ListItemIcon className={classes.listItemIcon}>
<Icon />
</ListItemIcon>
<ListItemText className={classes.listItemText} primary={opt.title} />
</NavLink>
</ListItem>
))}
</List>
</Drawer>
right panel code
<Box className={classes.content}>
<Switch>
<Route exact path={path} component={GeneralSettings} />
<Route path={`${path}/pDefaults`} component={ProductDefaults} />
<Switch>
</Box>

There's ClickAwayListener component in MaterialUI, looks like it's what you need.

One option to solve this is to use a hook that detects when you click outside of an element. Here's an example of such hook. The ref should be assigned to the topmost React Element in your left panel

Related

How to make Sider selected key be responsive?

What I'm trying to do is get which is the key of the sider the user is using (without ReactDOM and with functional components)
my sample code here:
export default function DrawerSider() {
const history = useHistory();
const [selectedKey, setSelectedKey] = useState("sub1")
const handleSelectKey = function(key, history_string) {
setSelectedKey(key)
history.push(history_string)
console.log(key)
}
return (
<Sider width={200} className="site-layout-background">
<Menu
defaultSelectedKeys={selectedKey}
mode="inline"
style={{ height: "100%", borderRight: 0 }}
>
<Menu.Item
key="sub1"
icon={<HomeOutlined />}
onClick={() => handleSelectKey("sub1","/dashboard/resumo")}
>
Dashboard
</Menu.Item>
<SubMenu key="sub2" icon={<UserOutlined />} title="Usuários">
<Menu.Item
key="1"
icon={<PlusCircleOutlined />}
onClick={() => handleSelectKey("1","/usuarios/novo")}
>
Adicionar usúario
</Menu.Item>
<Menu.Item
key="2"
icon={<TableOutlined />}
onClick={() => handleSelectKey("2","/usuarios/todos")}
>
Todos usúarios
</Menu.Item>
</SubMenu>
<SubMenu key="sub3" icon={<FormOutlined />} title="Formulários">
<Menu.Item
key="3"
icon={<PlusCircleOutlined />}
onClick={() => handleSelectKey("3","/formularios/novo")}
>
Adicionar formulário
</Menu.Item>
</SubMenu>
</SubMenu>
</Menu>
</Sider>
);
}
Obs: I'm using ant design lib
Someone knows how to make it works?
I tried to use a const on click event to set a state of the selected key , but it didn't work
There is no onClick on Menu.Item
only Menu component has onClick or onSelect, both will give you callback of the "clicked" key, the differences is onClick is for any menu item click (including expanding menu) while on select is when you select an actual menu item.
There are 2 ways to get the history string:
1 - use the history string as menuItem key:
the issue would be you cannot have 2 menu item that has same history key
2 - have a map which maps the menu key to history string
see below demo:
https://codesandbox.io/s/inline-menu-antd-4-18-2-forked-bgwyj?file=/index.js
const handleOnSelect = ({ item, key, keyPath, selectedKeys, domEvent }) => {
console.log(item);
console.log(keyPath);
console.log(selectedKeys);
console.log(domEvent);
handleSelectKey(key);
};
...
..
<Menu
onSelect={handleOnSelect}
...
>
...
</Menu>
You need to save the selected key as part of the component's state. You can set the current selected key as part of the onClick.
const selectedKey, setSelectedKey = useState("defaultSelectedKey")
Not sure what the useHistory hook is for but if you need to set that onClick as well as the selectedKey, move it all into one function like so
const handleSelectKey = function(key, history_string) {
setSelectedKey(key)
history.push(history_string)
}
...
onclick={() => handleSelectedKey("3", "/usuarios/novo")}
https://ant.design/components/menu/
EDIT
Based on research into your design library, your onclick and handle select must be at the top level of the menu component. Just copy the syntax that they use in their example code.
https://ant.design/components/menu/

Routing for persistant drawer in material UI

I want to use this as a navigation for my page ,
but i cant find out how to Link in the index properly,
I already tried to put l the items apart but that didnt work
whit ,,
and
<List>
{['Home' , 'About', 'Services', 'Contact'].map((text, index) => (
<ListItem button key={text} >
<ListItemIcon>
{index === 0 && <HomeOutlinedIcon /> }
{index === 1 && <InfoOutlined/>}
{index === 2 && <SettingsOutlinedIcon />}
{index === 3 && <MailIcon/>}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
Can someone, Show me how to correctly route my navigation in this list.
Just create pages for 'Home' , 'About', 'Services', 'Contact' and use the navigate function from reach-router.
You just have to declare your routes in the app.js with your path="/app/home" etc and the component you want to render to the DOM, then use navigate("/app/home") so you should change your array to an object with the good route on each item like this
const fieldsNames = [
{ value: "Home", icon: <SvgIcon component={SearchIcon} />, path: "/app/home" },
]
and map on those item with an onClick.

React Router. Why, when changing the route, the component is rendered 2 times, which causes 2 requests to the server?

I am using ReactTransitionGroup with ReactRouter.
The goal is to reroute smoothly from one component to another. The problem - is that the component is rendered twice.
An example of a component (view) that renders twice
I am using the console to check. You might say that this is not critical. But, the problem is that because of this problem, 2 requests go to the server (one extra). Therefore, it is desirable for me to get rid of this bug.
This is the component itself - the switch
When switching a route, the console issues logs twice
I need to figure out why the side effect is being called twice. If there is not enough information, then write comments. I will try to answer as quickly as possible.
Here's an example from the documentation. I have achieved this effect, but the problem has already been described.
UPD: I remember very well that once it worked like a clockwork. But, probably, I myself did not notice that I changed something, which led to this problem.
UPD: If you need a code, then please, the required elements:
const TabList = ({ tabs }) => {
return (
<nav className="p-my-company__tabs">
{tabs.map(({ to, label, id }) => (
<NavLink to={to} key={id}>
<div>{label}</div>
</NavLink>
))}
</nav>
);
};
const TabViews = ({ tabViews }) => {
const location = useLocation();
return (
<div className="p-my-company__views">
<TransitionGroup>
<SwitchTransition mode="out-in">
<CSSTransition
key={location.pathname}
classNames={{
enter: 'move-enter',
enterActive: 'move-enter-active',
exit: 'move-exit',
}}
timeout={100}>
<Switch>
{tabViews.map(({ path, Component, id }) => (
<Route path={path} render={() => <Component />} key={id} />
))}
</Switch>
</CSSTransition>
</SwitchTransition>
</TransitionGroup>
</div>
);
};
<div className="p-my-company__panel">
<TabList
tabs={[
{ to: ROUTES.COMMON_INFO, label: 'Общая информация', id: 1 },
{ to: ROUTES.SHOWCASE, label: 'Моя витрина', id: 2 },
]}
/>
<TabViews
tabViews={[
{ path: ROUTES.COMMON_INFO, Component: CommonView, id: 1 },
{ path: ROUTES.SHOWCASE, Component: ShowCaseView, id: 2 },
]}
/>
</div>
const ShowCase = () => {
useEffect(() => {
console.log(2);
}, []);
return <div>ShowCase</div>;
};
Looks like the Switch component from React Router and React Transition Group don't work well together. The docs recommend avoiding the usage of the Switch component and passing a function to the Route's children prop. Since the function will be called regardless of whether there is a match or not, you can conditionally render Component if there's one.
<>
{tabViews.map(({ path, Component }) => (
<Route exact path={path} key={path}>
{({ match }) => (
<TransitionGroup>
<SwitchTransition mode="out-in">
<CSSTransition
in={match != null}
classNames={{
enter: 'move-enter',
enterActive: 'move-enter-active',
exit: 'move-exit',
}}
timeout={100}
unmountOnExit
key={location.pathname}
>
<div className="page">{match && <Component />}</div>
</CSSTransition>
</SwitchTransition>
</TransitionGroup>
)}
</Route>
))}
</>

Material UI+React hovering on tabs will not open and close properly

currently I am working on a project with React and Material UI. I want to hover on tabs that will open an menu, but this doesn't really work. I am hoping that you guys can help me (and maybe tell me if I'm approaching this correctly)
Where my tabs are basing of: https://imgur.com/a/HeiL2xo
My current project: https://imgur.com/a/Ik5NEkF
AppBarTop class
class AppBarTop extends Component {
state = {
value: 0,
open: false,
anchorEl: null
};
handleMenuClick = (index) => {
}
handleMenuOpen = (index, event) => {
const {currentTarget} = event;
this.setState({
open: !this.state.open,
anchorEl: currentTarget,
value: index
})
};
handleMenuClose = () => {
this.setState({
open: false,
anchorEl: null,
})
}
handleInputSearch = () => {
};
render() {
const {classes} = this.props;
const {anchorEl, open} = this.state;
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<img src={buddies} alt={"buddies"} height={50} width={50}/>
<div className={classes.grow}/>
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon/>
</div>
<InputBase
placeholder="Search…"
onChange={this.handleInputSearch}
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
/>
</div>
<div className={classes.grow}/>
<List>
{TopMenu.map((item, index) => (
<Tab key={index} component={Link} to={{pathname: item.pathname}}
classes={{root: classes.tabItem}} label={item.label}/>
))}
</List>
</Toolbar>
<Paper className={classes.grow}>
<Tabs
value={this.state.value}
indicatorColor="primary"
textColor="primary"
centered>
{BottomMenu.map((item, index) => (
<Tab
key={index}
onMouseOver={this.handleMenuOpen.bind(this, index)}
data-key={index}
classes={{root: classes.tabItem}}
label={item.label}
aria-owns={open ? 'menu-list-grow' : undefined}
aria-haspopup={"true"}/>
))}
</Tabs>
<Popper open={open} anchorEl={anchorEl} id="menu-list-grow">
<Paper>
<MenuList>
{BottomMenu[this.state.value].items.map((item, index) => (
<MenuItem key={index} onClick={this.handleMenuClose}>{item}</MenuItem>
))}
</MenuList>
</Paper>
</Popper>
</Paper>
</AppBar>
</div>
);
}
}
export default withStyles(styles)(AppBarTop)
The key problem here is that the onMouseOver event handler is fired multiple times as you move around the <Tab> component. Your handleMenuOpen function is not built to handle this.
I've replicated your issue in a CodeSandbox here: https://codesandbox.io/s/qkw8rr4mk4
The following 3 points will fix your menu issues:
Change handleMenuOpen to be functional by explicitly setting open: true
Use onMouseEnter rather than onMouseOver. This is not required but it makes for more predictable functionality as onMouseEnter is only called once
To automatically close your menu when your mouse leaves them add the onMouseLeave={this.handleMenuClose.bind(this)} property to your parent <div> component
A CodeSandbox with the above 3 points implemented can be found at: https://codesandbox.io/s/6x9w9m6n7r

Onsen UI + React Navigator.pushPage not a function?

Using the Onsen playground & React Combining Navigator and Tabbar example, I made this - https://media.giphy.com/media/fjxPFluObAs5P3cXms/giphy.gif
I wanted to change the position of the button and input field for where you add more users, so I used a fab. Whenever I call the navigator.pushPage it says TypeError: navigator.pushPage is not a function.
The code behind it is:
fabHandleClick() {
navigator.pushPage({ component: ItemForm, props: { key: 'itemForm' } });
}
render() {
return (
<Page>
<List
dataSource={this.props.users}
renderRow={this.renderList.bind(this, this.props.users, this.props.added, "Add", true)}
/>
<br />
<hr />
<br />
<List
dataSource={this.props.added}
renderRow={this.renderList.bind(this, this.props.added, this.props.users, "Remove", false)}
/>
<Fab
onClick={this.fabHandleClick}
position='bottom right'>
<Icon icon='md-face' />
</Fab>
</Page>
);
}
Thanks in advance!
<Page /> should have access to the navigator object either by being wraped in a function or as a prop
Wrapped example:
renderPage = (route, navigator) => (<Ons.Page key={route.title} renderToolbar={() => this.renderToolbar(route, navigator)}>
<section style={{ margin: '16px', textAlign: 'center' }}>
<Ons.Button onClick={() => this.fabHandleClick(navigator)}>
Push Page
</Ons.Button>
</section>
</Ons.Page>);
fabHandleClick(navigator) {
navigator.pushPage({ component: ItemForm, props: { key: 'itemForm' } });
}

Categories