How to access props in function? - reactjs

I have problem with accessing props from function.
Here is my code(the component where all this is contained is also a function):
const [anchorEl, setAnchorEl] = React.useState(null);
function handleClick(event) {
setAnchorEl(event.currentTarget);
}
function handleClose(val) {
setAnchorEl(null);
this.props.updateSelected(val)
}
return (
<div>
<IconButton onClick={handleClick}>
<FilterListIcon/>
</IconButton>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={() => {handleClose(1).bind(this)}}>Содержит текст</MenuItem>
<MenuItem onClick={() => {handleClose(2)}}>Не содержит текст</MenuItem>
<MenuItem onClick={() => {handleClose(3)}}>Начинается с</MenuItem>
<MenuItem onClick={() => {handleClose(4)}}>Заканчивается на</MenuItem>
</Menu>
</div>
I have tried binding as you see but it did't work.
Here is how I pass this prop to the component from another component:
<FilterOptions updateSelected={this.updateSelectedValue}/>
And the prop itself:
updateSelectedValue = async (val) => {
await this.setState({selectedValue: val});
console.log(this.state.selectedValue)
};

You can destructure the prop from the functional component arguments. And since all your other functions are defined inside your component, you can access that prop from anywhere.
I'll give your component a name of Test:
const Test = ({ updateSelected }) => {
const [anchorEl, setAnchorEl] = React.useState(null);
function handleClick(event) {
setAnchorEl(event.currentTarget);
}
function handleClose(val) {
setAnchorEl(null);
updateSelected(val)
}
return (
<div>
<IconButton onClick={handleClick}>
<FilterListIcon/>
</IconButton>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={() => {handleClose(1).bind(this)}}>Содержит текст</MenuItem>
<MenuItem onClick={() => {handleClose(2)}}>Не содержит текст</MenuItem>
<MenuItem onClick={() => {handleClose(3)}}>Начинается с</MenuItem>
<MenuItem onClick={() => {handleClose(4)}}>Заканчивается на</MenuItem>
</Menu>
</div>
}

Related

How do I add a custom attribute to the generated React Menu item?

I'd to add a id or some attribute to the MenuItem, something like <MenuItem id="foo" onClick={handleClose}>Copy Ctrl+C</MenuItem> so that I can acess it from event.target in the context menu event handler in my TreeView.
Currently the code look like this:
export default function FileSystemNavigator() {
const [contextMenu, setContextMenu] = React.useState<{
mouseX: number;
mouseY: number;
} | null>(null);
const handleContextMenu = (event: React.MouseEvent) => {
// get the HTML element with the id, e.g., id="foo" in the attributes of event.target
event.preventDefault();
setContextMenu(
contextMenu === null
? {
mouseX: event.clientX + 2,
mouseY: event.clientY - 6
}
: // repeated contextmenu when it is already open closes it with Chrome 84 on Ubuntu
// Other native context menus might behave different.
// With this behavior we prevent contextmenu from the backdrop to re-locale existing context menus.
null
);
};
const handleClose = () => {
setContextMenu(null);
};
return (
<div onContextMenu={handleContextMenu} style={{ cursor: "context-menu" }}>
<TreeView
aria-label="file system navigator"
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
>
<TreeItem nodeId="1" label="Applications">
<TreeItem nodeId="2" label="Calendar" />
</TreeItem>
<TreeItem nodeId="5" label="Documents">
<TreeItem nodeId="10" label="OSS" />
<TreeItem nodeId="6" label="MUI">
<TreeItem nodeId="8" label="index.js" />
</TreeItem>
</TreeItem>
</TreeView>
<Menu
open={contextMenu !== null}
onClose={handleClose}
anchorReference="anchorPosition"
anchorPosition={
contextMenu !== null
? { top: contextMenu.mouseY, left: contextMenu.mouseX }
: undefined
}
>
<MenuItem onClick={handleClose}>Copy Ctrl+C</MenuItem>
<MenuItem onClick={handleClose}>Delete</MenuItem>
<MenuItem onClick={handleClose}>Move</MenuItem>
<MenuItem onClick={handleClose}>Email</MenuItem>
</Menu>
</div>
);
}
Here's a live code example
There are a few options to achieve this:
by passing a different function to each of your MenuItem:
const handleCopy = () => {
// Do whatever you need
handleClose();
};
const handleDelete = () => {
// Do whatever you need
handleClose();
};
<MenuItem onClick={handleCopy}>Copy Ctrl+C</MenuItem>
<MenuItem onClick={handleDelete}>Delete</MenuItem>
you can use data- attributes:
const handleClose = (event) => {
const { action } = event.target.dataset
// Here can be a switch/case statement to iterate the action
setContextMenu(null);
};
<MenuItem data-action="copy" onClick={handleClose}>
Copy Ctrl+C
</MenuItem>
just use the inline function:
<MenuItem onClick={() => handleClose({action: 'copy'})}>Copy Ctrl+C</MenuItem>
One way you can have a span tag and pass an id to it inside your MenuItem like this :
<MenuItem onClick={handleClose}>
<span id="Copy Ctrl+C">Copy Ctrl+C </span>
</MenuItem>
<MenuItem onClick={handleClose}>
<span id="Delete">Delete </span>
</MenuItem>
<MenuItem onClick={handleClose}>
<span id="Move">Move </span>
</MenuItem>
<MenuItem onClick={handleClose}>
<span id="Email">Email </span>
</MenuItem>

Open a modal (or dialogue) from a dropdown asking the user if they would like to take an action

I would like to open a modal (or dialogue) when a user selects an option from a dropdown menu.
Eventually there will be a few options in the dropdown, and different dialogues/modals will be called and rendered depending on which dropdown option has been clicked. For now - how do I get the modal/dialogue to open with dropdown menu events?
I'm currently using the handleClose handler to attempt to open a dialogue since that event should be easy to grab and use right out of the docs for MUI Menu and MenuItem.
The origination call to the DropdownMenu component (which works well and shows the dropdown menu) occurs in a table through the click of an icon
<DropdownMenu options={['Show modal or dialogue to the user']}>
<MoreHorizIcon />
</DropdownMenu>
The DropdownMenu component (also working well itself, except for not triggering the dialogue/modal) looks like this
interface IProps extends Omit<unknown, 'children'> {
children: any;
options: string[];
}
const ITEM_HEIGHT = 48;
const DropdownMenu = ({ children, options }: IProps) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const showModal = () => {
return (
<AlertDialog />
)
}
const handleClose = () => {
//the native alert dialogue works well
alert('I want to show a dialog or modal the same way this alert shows up and ask the user if they are sure they want to delete something')
//why isn't the custom alert dialog being called?
showModal();
setAnchorEl(null);
};
return (
<div>
<IconButton
aria-label="more"
id="more-button"
aria-controls="long-menu"
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleClick}
>
{children}
</IconButton>
<Menu
id="dropdownmenu"
MenuListProps={{
'aria-labelledby': 'more-button'
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
PaperProps={{
style: {
maxHeight: ITEM_HEIGHT * 4.5,
width: '20ch'
}
}}
>
{options.map(option => (
<MenuItem key={option} onClick={handleClose} >
{option}
</MenuItem>
))}
</Menu>
</div>
);
};
export default DropdownMenu;
And the example starter dialogue I am using to get the ball rolling looks like this
const AlertDialog = () => {
const [open, setOpen] = React.useState(true);
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Sweet Filler Dialog"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>NO</Button>
<Button onClick={handleClose} autoFocus>
YES
</Button>
</DialogActions>
</Dialog>
</div>
);
}
You can use a state variable to trigger the modal in the DropdownMenu component, whenever you wanted to show the dialog/modal.
const [isModalOpen, setIsModalOpen] = React.useState(false);
and then in the handleClose, you can update the modal state to open the modal.
const handleClose = () => {
setIsModalOpen(true)
setAnchorEl(null);
};
Then somewhere in your JSX of DropdownMenu, you can conditionally render the AlertDialog component like this
{isModalOpen ? <AlertDialog open={isModalOpen} setOpen={setIsModalOpen} /> : null}
Finally, update your AlertDialog component to use props to handle the closing of the modal.
const AlertDialog = ({ open, setOpen }) => {
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Sweet Filler Dialog"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>NO</Button>
<Button onClick={handleClose} autoFocus>
YES
</Button>
</DialogActions>
</Dialog>
</div>
);
}

How to get the key on click event

I have the following react component:
type MobileNavProp = RouteTableProp
export default function MobileNav({routes}: MobileNavProp) {
const classes = useStyles()
const [drawerOpen, setDrawerOpen] = useState<boolean>(false)
const handleOnClickItem = (event: React.MouseEvent<HTMLDivElement>): void => console.log(event.target)
return (
<React.Fragment>
<IconButton
className={classes.icon}
color="inherit"
aria-label="Open navigation"
edge="end"
onClick={() => setDrawerOpen(true)}
>
<MenuRoundedIcon fontSize="large"/>
</IconButton>
<SwipeableDrawer open={drawerOpen}
onClose={() => setDrawerOpen(false)}
onOpen={() => console.log("Drawer is open")}
disableBackdropTransition={!iOS}
anchor="top"
disableDiscovery={iOS}
>
<List subheader={
<ListSubheader component="div" id="logo" className={classes.company}>
<img className={classes.logo} src={logo} alt="databaker-logo"/>
<Typography variant="h6" className={classes.title}>
DATABAKER
</Typography>
</ListSubheader>
}>
{
routes.map((rt, index) => (
<ListItem
divider
key={index}
button
onClick={handleOnClickItem}
>
<ListItemText primary={rt.name}/>
</ListItem>
))
}
</List>
</SwipeableDrawer>
</React.Fragment>
)
}
When the user click on ListItem(the handler function is handleOnClickItem), then it shows me:
<span class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock">ABOUT US</span>
However, I would like to get the clicked index that I have provided:
<ListItem
divider
key={index}...
How to get it?
You can make handleOnClickItem a higher-order function that takes the index as a parameter, and call it:
const makeHandleOnClickItem = (i: number) => (event: React.MouseEvent<HTMLDivElement>) => {
console.log(event.target);
console.log(i);
};
and change
onClick={handleOnClickItem}
to
onClick={makeHandleOnClickItem(index)}

Update list item in material ui?

I have a project in react, and I'm building a simple todo app. When an item in the list is clicked, I want to update the value. My code looks like this:
export default function ShowTodos () {
const [todos, setTodos] = React.useState<ITodo[]>([]);
const [selectedTodo, setSelectedTodo] = React.useState<ITodo>({});
const [dialogState, setDialogState] = React.useState<boolean>(false);
const confirm = useConfirm();
useEffect(() => {
getTodos()
.then(({data: {todos}}: ITodo[] | any) => {
const todoList = todos
setTodos(todoList)
})
.catch((err: Error) => console.log(err))
})
const handleConfirmation = (id: string) => {
confirm({ description: 'This action is permanent!' })
.then(() => { deleteTodoById(id).then(r => )})
}
return (
<List>
{todos.map((todo: ITodo ) =>
(
<ListItem onClick={() => {
console.log("hh", todo);
setDialogState(!dialogState)
}
} key={todo._id}>
<ListItemText
primary={todo.text}
/>
<IconButton edge="end" aria-label="delete" onClick={() => handleConfirmation(todo._id)}>
<DeleteIcon />
</IconButton>
<Dialog open={dialogState} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Update</DialogTitle>
<DialogContent>
<TextField
defaultValue={todo.text}
autoFocus
margin="dense"
id="name"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button color="primary" onClick={() => setDialogState(false)}>
Cancel
</Button>
<Button color="primary" onClick={() => updateTodo(selectedTodo)}>
Update
</Button>
</DialogActions>
</Dialog>
</ListItem>
)
)}
</List>
);
}
However the odd thing is that the defaultValue when the item is clicked is always the last item in the list. How am I to change it to be the text of the item clicked?
Thanks!
You need separate state of each todo item to new Component
The main problem in this piece of code:
<ListItem onClick={() => {
console.log("hh", todo);
setDialogState(!dialogState)
}
} key={todo._id}>
onClick you toggling dialog, but not specify selectedTodo.
try something like that:
onClick={() => {
console.log("hh", todo);
setSelectedTodo(todo);
setDialogState(!dialogState);
}
and I guess you should define updateTodo

How can i call a function inside another Component with React?

I have a component (Navigation.js) which imports another component (Dialog.js). I want that if I react to a click event, call a function in the dialog component (handleClickOpen ()). But I don't know how to do that. So what i have to do ?
Navigation.js
export default function SimpleBottomNavigation() {
return (
<BottomNavigation
value={value}
onChange={(event, newValue) => {
setValue(newValue);
}}
showLabels
className={classes.root}
>
<BottomNavigationAction
label="Home"
onClick={'HERE I WANT TO CALL THE FUNCTION IN THE DIALOG COMPONENT'}
icon={<RestoreIcon />}
/>
<BottomNavigationAction label="Neuer Plan" icon={<FavoriteIcon />} />
<BottomNavigationAction label="Azubis" icon={<LocationOnIcon />} />
</BottomNavigation>
);
}
Dialog.js
export default function CustomizedDialogs() {
const [open, setOpen] = React.useState(false);
*/THIS FUNCTION I WANT TO CALL FROM NAVIGATION.JS */
const handleClickOpen = () => {
setOpen(true);
};
[...]
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open dialog
</Button>
<Dialog
onClose={handleClose}
aria-labelledby="customized-dialog-title"
open={open}
>
<DialogTitle id="customized-dialog-title" onClose={handleClose}>
Modal title
</DialogTitle>
<DialogContent dividers>
</Dialog>
</div>
);
}
There is a aimple way to use child's functions, with functional components it's forwardRef and useImperativeHandle, along those lines:
Navigation (parent)
function Navigation() {
const dialogRef = useRef();
return(
<button onClick={() => dialogRef.current.handleClickOpen()}>
Click me!
</button>
);
}
Dialog (child)
const Dialog = forwardRef((props, ref) => {
useImperativeHandle(ref, () => {
const handleClickOpen = () => {
//your implementation
};
});
return (...);
});
If I over-simplified, please let me know :)

Resources