React Material UI Button component with hookrouter - reactjs

I am trying to migrate a class component React app to functional components with hooks.
When using class components I use to pass a Link component to a Button component since I was using react-router-dom library.
But now I am trying to use Paratron/hookrouter library for routing but I get an error when passing an A component to the Button:
TypeError: props.href is undefined
My code now looks like this:
import React, { Fragment } from 'react';
import { Grid, Button } from '#material-ui/core';
import { A } from 'hookrouter';
import './styles.css';
const Home = props => {
return (
<Fragment>
<Grid container spacing={24} className="landing">
<Grid item xs={6}>
<Button
variant="contained"
color="primary"
size="large"
component={A}
to="/login"
>Login</Button>
</Grid>
<Grid item xs={6}>
<Button
variant="contained"
color="secondary"
size="large"
component={A}
to="/contact"
>Contact us</Button>
</Grid>
</Grid>
</Fragment>
);
}
export default Home;
I guess this A component does not contain an href property. Not sure how to proceed. Any comments will be appreciated.

You need to provide 'href' prop, not 'to' prop.
<Fragment>
<Grid container spacing={24} className="landing">
<Grid item xs={6}>
<Button
variant="contained"
color="primary"
size="large"
component={A}
href="/login" //href
>Login</Button>
</Grid>
<Grid item xs={6}>
<Button
variant="contained"
color="secondary"
size="large"
component={A}
href="/contact" //href
>Contact us</Button>
</Grid>
</Grid>
</Fragment>
I also had to wrap A with class component, since A is probably function component, and they can't hold a ref (which is required by button component prop)
You can refer to this working CodeSandbox demo

Related

Adding Dynamic States to React JS project

I am creating an emojipedia app where it is expected to open a Modal, which contains the description of the emoji, when an emoji is pressed. As far as I know, to do so, I need to map the description(contained in emojipedia.js file) of the emoji to the EmojiContainer component in Components folder.
Here comes my problem where when I press a emoji, it is getting hanged. Why is this happening and how to fix this???
THANKS IN ADVANCE.
You are using a single state on EmojiContainer to control all modals in your emoji list. As a consequence, when you try and open a modal, all modals open. A better option would be to encapsulate all logic relative to a single modal in a separate, reusable component:
export default function Emoji({ item }) {
const [open, setOpen] = useState(false);
return (
<Grid item lg={2} md={3} xs={6}>
<ImageButton onClick={() => setOpen(true)}>
<CardMedia
sx={{
"&:hover": {
transform: "scale(1.3)"
}
}}
component="img"
height="100"
image={item.link}
alt="emoji"
/>
</ImageButton>
<Modal
open={open}
onClose={() => setOpen(false)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Typography sx={style} variant="p">
{item.desc}
</Typography>
</Modal>
</Grid>
);
}
As you see this component has its own state and controls its own modal. In your EmojiContainer you can use it like this:
export default function EmojiContainer() {
return (
<Grid>
{emojipedia.map((item, index) => (
<Grid key={index} container>
<Emoji item={item} />
</Grid>
))}
</Grid>
);
}
From what I see you'll also need to adjust the modal styling. Here's the updated codesandbox

Is it possible to make component style dependent on state without re-rendering?

I am working with the component below - I need the button to be contained when the filter state corresponds to it and outlined otherwise.
The way I do it now, the component re-renders every time the state is changed - I see why this is happening. However, I wonder if there would be a way to achieve the same functionality without referring to the filter state in this component? It is not a great user experience if the buttons disappear every time the state changes.
function FilterButtons({ filter, setFilter }) {
const classes = useStyles();
return (
<div className={classes.heroButtons}>
<Grid container spacing={2} justify="center">
<Grid item>
<Button
variant={filter === "All" ? "contained" : "outlined"}
color="primary"
onClick={() => setFilter("All")}
>
All
</Button>
</Grid>
<Grid item>
<Button
variant={filter === "Blue" ? "contained" : "outlined"}
color="primary"
onClick={() => setFilter("Blue")}
>
Blue
</Button>
</Grid>
<Grid item>
<Button
variant={filter === "Red" ? "contained" : "outlined"}
color="primary"
onClick={() => setFilter("Red")}
>
Red
</Button>
</Grid>
<Grid item>
<Button
variant={filter === "Green" ? "contained" : "outlined"}
color="primary"
onClick={() => setFilter("Green")}
>
Green
</Button>
</Grid>
</Grid>
</div>
);
}
You should have a look at styled components. This is exactly what you're describing in your question. You can pass a props to a styled components and it will use this props to update its style without rendering.

How do I prevent Material UI Dialog from being dismissed upon clicking the backdrop?

I have a React JS app that uses the Dialog component and I cannot seem to find any documentation on how I can prevent the dialog from being automatically dismissed by merely clicking the backdrop. I have an explicit action within the dialog that I want to use for control of the dismissal.
I have tried reading the docs and of course searching here but am not finding anything helpful or that contains an example. Any help is appreciated; this is my first time using React.
<Dialog onClose={handleClose} aria-labelledby="simple-dialog-title" open={open}>
<DialogTitle id="simple-dialog-title">Uploading Media To Server</DialogTitle>
<React.Fragment>
<Grid container alignItems="center" justify="center">
<img src={LoadingGif} width="150" />
</Grid>
</React.Fragment>
</Dialog>
There was mention of this being a possible duplicate of How to handle "outside" click on Dialog (Modal) with material-ui but do not find it helpful as I am using a Dialog component instead of a Modal.
Material 4
Try this:
<Dialog onClose={handleClose} aria-labelledby="simple-dialog-title"
open={open} onBackdropClick="false">
<DialogTitle id="simple-dialog-title">Uploading Media To Server</DialogTitle>
<React.Fragment>
<Grid container alignItems="center" justify="center">
<img src={LoadingGif} width="150" />
</Grid>
</React.Fragment>
</Dialog>
You can also achieve it setting disableBackdropClick="true", which maybe is more appropriate for your use case.
Material 5
onBackdropClick and disableBackdropClick were deprecated in Material v5, use this instead:
<Dialog onClose={handleClose} aria-labelledby="simple-dialog-title"
open={open}>
<DialogTitle id="simple-dialog-title">Uploading Media To Server</DialogTitle>
<React.Fragment>
<Grid container alignItems="center" justify="center">
<img src={LoadingGif} width="150" />
</Grid>
</React.Fragment>
</Dialog>
And checking whether the backdrop was clicked in the onClose handler:
const handleClose = (event, reason) => {
if (reason && reason == "backdropClick")
return;
myCloseModal();
}
Try to remove onClose prop to Dialog component, it will solve your problem.
Also you can trigger handleClose on cancel button.
It was a quick fix for me.
<Dialog aria-labelledby="simple-dialog-title" open={open}>
<DialogTitle id="simple-dialog-title">Uploading Media To Server</DialogTitle>
<React.Fragment>
<Grid container alignItems="center" justify="center">
<img src={LoadingGif} width="150" />
</Grid>
</React.Fragment>
</Dialog>

Material UI button rendered with span that is not clickable

I am building a site using material-UI and have run into a bit of a snag. It seems that the button is being rendered with a span element around the button's text. This makes it so the click event only fires when you click outside the span element.
As you can imagine it's not the greatest UI to have users click buttons that don't do anything. I am sure that I am not the first one to go through this, is there a way that I can propagate the event down to the child element programmatically? Below is my component snipit:
import withRoot from '../onepirate/modules/withRoot';
// --- Post bootstrap -----
import React from 'react';
import { useState, useStyles } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Grid from '#material-ui/core/Grid';
import Link from '#material-ui/core/Link';
import Typography from '../onepirate/modules/components/Typography';
import AppFooter from '../onepirate/modules/views/AppFooter';
import AppForm from '../onepirate/modules/views/AppForm';
import Button from '#material-ui/core/Button';
import MuiTextField from '#material-ui/core/TextField';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemText from '#material-ui/core/ListItemText';
import ListItemAvatar from '#material-ui/core/ListItemAvatar';
import Avatar from '#material-ui/core/Avatar';
import AddCircleIcon from '#material-ui/icons/AddCircle';
import WorkIcon from '#material-ui/icons/Work';
import BeachAccessIcon from '#material-ui/icons/BeachAccess';
import Divider from '#material-ui/core/Divider';
const Tickets = props => {
const useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1),
backgroundColor: "#ff3366",
color: "#000000"
},
input: {
display: 'none',
},
ListItemText: {
marginLeft: 105,
}
}));
const classes = useStyles();
return (
<React.Fragment>
<AppForm>
<React.Fragment>
<Typography variant="h3" gutterBottom marked="center" align="center">
Order Tickets
</Typography>
<Typography variant="body2" align="center">
<Link href="/premium-themes/onepirate/sign-in/" underline="always">
Already have an account?
</Link>
</Typography>
</React.Fragment>
<Grid container spacing={2}>
<Grid item xs={12} sm={12}>
<List className={classes.root}>
<ListItem>
<Button
variant="contained"
className={classes.button}
onClick={props.quantity}
id="basic">
Add
</Button>
<ListItemText
primary="Basic"
secondary="$450"
className={classes.ListItemText}/>
{props.init.basic}
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<Button
variant="contained"
className={classes.button}
onClick={props.quantity}
id="exec" >
Add
</Button>
<ListItemText
primary="Executive"
secondary="$550"
className={classes.ListItemText}/>
{props.init.exec}
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<Button
variant="contained"
className={classes.button}
onClick={props.quantity}
id="vip">
Add
</Button>
<ListItemText
primary="VIP"
secondary="$750"
className={classes.ListItemText}/>
{props.init.vip}
</ListItem>
</List>
<Typography variant="h2">
{`Total: $ ${(props.init.basic * 450) + (props.init.exec * 550) + (props.init.vip * 750)}`}
</Typography>
<Button
className={classes.button}
size="large"
color="secondary"
fullWidth
onClick={props.price}
id=""
>
{'Proceed to Checkout'}
</Button>
</Grid>
</Grid>
</AppForm>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(Tickets);
And the rendered HTML output of one of the buttons:
<button class="MuiButtonBase-root-307 MuiButton-root-290 makeStyles-button-87 MuiButton-contained-298"
tabindex="0"
type="button"
id="exec">
<span class="MuiButton-label-291">Add</span>
<span class="MuiTouchRipple-root-391"></span>
</button>
It seems that Material UI v4 accepts both e.target.value and e.currentTarget.value.
However, if you use e.target.value to deliver buttons' value, and click on the span node (nested inside of button), the value won't be taken from the button node that you assigned it to, but the span HTML element that does not have the value property and so the value is undefined.
To remedy the above, use e.currentTarget.value as it seems to pick up the value property from the button, even if you click on the span node.
Below sandbox should show you the difference in using e.target.value and e.currentTarget.value with material UI buttons.
https://codesandbox.io/s/elated-glitter-wqtt3?file=/src/App.js
Open up the console and click interchangeably on span and button elements and observe logs when you do that.
I have not tested the latest v5 from Material UI, it could be that they changed (improved?) this behaviour.
try wrapping span and button inside a div or other wrapper component and call onClick on that

How to add search bar in AppBar of material-ui?

I want to implement Search bar in the center of App bar of material-ui. I have tried all possible ways and I have referred this code snippet , but can't find a solution for it.
My code snippet is
<div>
<MuiThemeProvider muiTheme={muiTheme} class="navbar">
<AppBar
title='Module Name'
onTitleClick={handleClick}
iconElementRight={<FlatButton label='LogOut' />}
onClick = {handleclick}
/>
</MuiThemeProvider>
</div>
It will be helpful if I get any solution for it.
You can use children property to add any node in AppBar, like this:
<AppBar
title="Title"
children= {
<input />
}
/>
Use styling on input field, check the working codesandbox.
you can add a ToolBar contains a Textfield
you can check documentation ,this is a Demo
Using only Material UI
<AppBar>
<Toolbar>
<Input
type="search"
/>
</Toolbar>
</AppBar>
You can check the docs here.
Installing another framework.
npm i material-ui-search-bar
A basic code snippet can be found on the main page.
import SearchBar from "material-ui-search-bar";
// *snip*
return (
<SearchBar
value={this.state.value}
onChange={(newValue) => this.setState({ value: newValue })}
onRequestSearch={() => doSomethingWith(this.state.value)}
/>
);
You can check the docs here.
Try this
<AppBar>
<Toolbar>
<InputBase placeholder="Search for products, brands and more" />
</Toolbar>
</AppBar>
Pass a prop in the AppBar component
<AppBar
title="Title"
showSearch= {
<Toolbar>
<InputBase placeholder="Search" />
</Toolbar>
}
/>
You can handle AppBar component based on customName prop like if you're passing this prop then it should show Search else not.

Resources