material-ui backdrop -- fails with invalid hook call - javascript

I have been trying to use material ui backdrop element in my code. I am referring to this example: https://mui.com/components/backdrop/#example
As soon as I introduce this code into my component, it fails with error message as below.
Unhandled Runtime Error Error: Invalid hook call. Hooks can only be
called inside of the body of a function component. This could happen
for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug
and fix this problem.
My code is as below.
import React, { useState } from 'react'
import { makeStyles } from '#material-ui/core/styles'
import { Box,Typography } from '#material-ui/core'
import CategoryCard from '../shop-categories/category-card'
import { useSelector } from 'react-redux';
//import NavigateBeforeIcon from '#material-ui/icons/NavigateBefore';
//import ProductsInCategory from '../../../pages/shops/product-in-category';
import {useRouter} from 'next/router'
import Backdrop from '#mui/material/Backdrop'
import CircularProgress from '#mui/material/CircularProgress';
import { withStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
heading : {
fontFamily: 'Roboto',
fontSize: 14,
fontStyle: 'normal',
fontWeight: 700,
lineHeight: 1,
color: props => props.pColor
}
})
export default function CategoryGrid(props) {
const { categories, shopId, title} = props;
const shopColors = useSelector(state => state.main.currentShop)
const classes = useStyles(shopColors);
const router = useRouter()
const [open, setOpen] = React.useState(false);
const handleClose = () => {
setOpen(false);
};
const gotoSubCategories = (shopId, id,cat_Name) => {
setOpen(true)
router.push(`/shops/shop-sub-categories?shop=${shopId}&category=${id}&categoryName=${cat_Name}`)
}
return (
<>
<div>
<Backdrop sx={{color: '#fff', zIndex: (theme) => theme.zIndex.drawer +1}}
open={open}
onClick={handleClose}
>
<CircularProgress color='inherit' />
</Backdrop>
</div>
<Typography variant="body2" style={{ fontWeight: 'bold' }}
className={classes.heading}>
{title}
</Typography>
<Box display="flex" mb={2} mx={1} flexDirection="column">
<Box display="flex" fontWeight='fontWeightBold' width={1/2} mb={1}>
</Box>
<Box display="flex" flexWrap="wrap">
{
categories.map((category, index) => {
return <CategoryCard key={index}
name={category.name}
onClick={() => gotoSubCategories(shopId, category.id,category.name)}
imageURL={category.url}
shopId={shopId} callParent={props.callParent}
/>
})
}
</Box>
</Box>
</>
)
}
If I remove out this portion, then it works fine.
<div>
<Backdrop sx={{color: '#fff', zIndex: (theme) => theme.zIndex.drawer +1}}
open={open}
onClick={handleClose}
>
<CircularProgress color='inherit' />
</Backdrop>
</div>
How to fix this?

Related

Why is the cursor selection to highlight text and perform copy/paste is not working on Chrome for my React app?

I am using Material UI (V4). There is a Customized SnackBar component being used to display the messages from backed, like success and failure messages. But, I am unable to copy the message shown on the snackbar component. This issue is seen only on Chrome (the text selection is working fine on Safari browser). Here is the sample code I'm using:
import React from 'react';
import Button from '#material-ui/core/Button';
import Snackbar from '#material-ui/core/Snackbar';
import MuiAlert from '#material-ui/lab/Alert';
import { makeStyles } from '#material-ui/core/styles';
import { Icon } from '#material-ui/core';
import SmsFailedIcon from '#material-ui/icons/SmsFailed';
const successIcon = () => (
<Icon>
<SmsFailedIcon />
</Icon>
);
const failedIcon = () => (
<Icon>
<SmsFailedIcon />
</Icon>
);
function Alert(props) {
return (
<MuiAlert
elevation={6}
variant="filled"
{...props}
iconMapping={{ success: successIcon, error: failedIcon }}
/>
);
}
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
'& > * + *': {
marginTop: theme.spacing(2),
},
},
}));
export default function CustomizedSnackbars() {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleClick = () => {
setOpen(true);
};
const handleClose = (event, reason) => {
setOpen(false);
};
return (
<div className={classes.root}>
<Button variant="outlined" onClick={handleClick}>
Open success snackbar
</Button>
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
<Alert onClose={handleClose} severity="success">
This is a success message!
</Alert>
</Snackbar>
</div>
);
}
Can anyone please advise ?

Why are my testing-library queries not working?

I am using Jest/React Testing Library(RTL) to to unit testing for the UI.
None of my RTL queries work.
It keeps telling me errors like
TestingLibraryElementError: Unable to find an element by: [data-testid="analysis-categories-header"]
no matter how I query it.
I have tried getBy*, findBy*(yes, with async...await), getByTestId and everything.
The only difference is that when I use findBy* I get an extra error that says
Error: Error: connect ECONNREFUSED 127.0.0.1:80
even screen.debug() is ignored and not working.
Out of the 13 tests I have, only one test work surprisingly. The queries go through.
test:
import '#testing-library/jest-dom';
import { render, screen } from '#testing-library/react';
import { HelmetProvider } from 'react-helmet-async';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import Login from '../src/pages/login';
test('renders logo and login box with "LOG IN" text and two buttons', () => {
const initState = {};
const mockStore = configureStore();
render(
<HelmetProvider>
<Provider store={mockStore(initState)}>
<Login />
</Provider>
</HelmetProvider>,
);
expect(screen.getByTestId('login-logo')).toBeVisible();
expect(screen.getByTestId('login-title')).toBeVisible();
expect(screen.getByRole('button', { name: 'Sign In' })).toBeVisible();
expect(
screen.getByRole('button', { name: 'Sign In With Intuit' }),
).toBeVisible();
});
component:
import { useAuth0 } from '#auth0/auth0-react';
import {
Box,
Button,
Card,
CardContent,
Container,
Typography,
} from '#material-ui/core';
import Link from 'next/link';
import { useRouter } from 'next/router';
import type { FC } from 'react';
import React, { useEffect } from 'react';
import { Helmet } from 'react-helmet-async';
import Logo from 'src/components/Logo';
import axios from 'src/lib/axios';
import gtm from 'src/lib/gtm';
const Login: FC = () => {
const router = useRouter();
const { isLoading, isAuthenticated, loginWithRedirect } = useAuth0();
useEffect(() => {
gtm.push({ event: 'page_view' });
}, []);
useEffect(() => {
if (!isLoading && isAuthenticated) {
router.push('/recommendations');
}
}, [isLoading, isAuthenticated]);
const handleIntuitLogin = async () => {
try {
const response = await axios.get('/auth/sign-in-with-intuit');
window.location = response.data;
} catch (e) {
throw new Error(e);
}
};
const handleAuth0Login = async () => {
try {
const response = await axios.get('/auth/sign-in-with-auth0');
window.location = response.data;
} catch (e) {
throw new Error(e);
}
};
return (
<>
<Helmet>
<title>Login</title>
</Helmet>
<Box
sx={{
backgroundColor: 'background.default',
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
}}
>
<Container maxWidth="sm" sx={{ py: '80px' }}>
<Box
sx={{
display: 'flex',
justifyContent: 'center',
mb: 8,
}}
>
<Link href="/login">
<Box>
<Logo height={100} width={300} />
</Box>
</Link>
</Box>
<Card>
<CardContent
sx={{
display: 'flex',
flexDirection: 'column',
p: 4,
}}
>
<Box
sx={{
alignItems: 'center',
display: 'flex',
justifyContent: 'space-between',
mb: 3,
}}
>
<div>
<Typography
color="textPrimary"
gutterBottom
variant="h4"
data-testid="login-title"
>
Log in
</Typography>
</div>
</Box>
<Box
sx={{
flexGrow: 1,
mt: 3,
}}
>
<Button
color="primary"
onClick={handleAuth0Login}
fullWidth
size="large"
type="button"
variant="contained"
>
Sign In
</Button>
</Box>
<Box sx={{ mt: 2 }}>
<Button
color="primary"
onClick={handleIntuitLogin}
fullWidth
size="large"
type="button"
variant="contained"
>
Sign In With Intuit
</Button>
</Box>
</CardContent>
</Card>
</Container>
</Box>
</>
);
};
export default Login;
The only difference is that this component (nextjs page) does not use redux thunk. This is how far I got to find the cause.

How to display a message instead of an empty array

My app has two buttons, one that displays information and another that hides it. This works as intended, however, some of the images loaded in from the API don't have any data available, and when the button to display the info is pressed, it returns a []
How would I target those that load with an empty array, and add in a string. For example "no information for this breed is available?"
App.js
import './App.css';
import './Dog.js';
import './index.css';
import "./Grid.js";
import NestedGrid from './Grid.js';
import Album from './Grid.js';
import AppLayout from './Grid.js';
function DogApp() {
return (
<div className="dogApp">
<AppLayout />
</div>
);
}
export default DogApp;
Grid.js
import * as React from 'react';
import AppBar from '#mui/material/AppBar';
import Button from '#mui/material/Button';
import Card from '#mui/material/Card';
import CardActions from '#mui/material/CardActions';
import CardContent from '#mui/material/CardContent';
import CardMedia from '#mui/material/CardMedia';
import CssBaseline from '#mui/material/CssBaseline';
import Grid from '#mui/material/Grid';
import Stack from '#mui/material/Stack';
import Box from '#mui/material/Box';
import Toolbar from '#mui/material/Toolbar';
import Typography from '#mui/material/Typography';
import Container from '#mui/material/Container';
import Link from '#mui/material/Link';
import { createTheme, ThemeProvider } from '#mui/material/styles';
import FetchAPI from './FetchAPI';
const cards = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const theme = createTheme();
export default function AppLayout() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<AppBar position="relative">
</AppBar>
<main>
{/* Hero unit */}
<Box
sx={{
bgcolor: 'background.paper',
pt: 8,
pb: 6,
}}
>
<Container maxWidth="sm">
<Stack
sx={{ pt: 4 }}
direction="row"
spacing={2}
justifyContent="center"
>
</Stack>
</Container>
</Box>
<Container sx={{ py: 8 }} maxWidth="md">
{/* End hero unit */}
<Grid container spacing={4}>
{cards.map((card) => (
<Grid item key={card} xs={12} sm={6} md={4}>
<Card
sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}
>
<FetchAPI />
<CardContent sx={{ flexGrow: 1 }}>
</CardContent>
</Card>
</Grid>
))}
</Grid>
</Container>
</main>
</ThemeProvider>
);
}
FetchAPI.js
import React, { useState, useEffect } from 'react'
const FetchAPI = () => {
const [show, setShow] = useState({});
const [data, setData] = useState([]);
const apiGet = () => {
const API_KEY = "";
fetch(`https://api.thedogapi.com/v1/images/search?limit=2&page=10&order=Desc?API_KEY=${API_KEY}`)
.then((response) => response.json())
.then((json) => {
console.log(json);
setData([...data, ...json]);
});
};
useEffect(() => { //call data when pagee refreshes/initially loads
apiGet();
}, []);
return (
<div>
{data.map((item, id) => (
<div>
<img alt="dog photos" className="dogImg" src={item.url}></img>
{show[id] === false ? <p>{JSON.stringify(item.breeds, null, '\t')}</p> : null}
<button onClick={() => setShow((prev) => ({ ...prev, [id]: false }))}>Learn more about this dog!</button>
<button onClick={() => setShow((prev) => ({ ...prev, [id]: true }))}>Hide information</button>
</div>
))}
</div>
)
}
export default FetchAPI;
You can compare the length of array like:
if(arr.length===0){
return "no information for this breed is available"
}
The above condition is just for understanding you can change the logic as well either you want to return or save in some state.

React JS / Material UI - Call method from imported Component

I'm trying to call the handleLoginOpen method from LoginModal.js, which I imported in File1.js.
File1 is actually a component nested in an AppBar, where i have an account IconButton that shows two MenuItem: Login or Register. If I press login I would like to show a Modal with the login form.
Here the two files:
File1.js:
import React from "react";
import { IconButton, Menu, MenuItem } from "#material-ui/core";
import AccountCircleIcon from "#material-ui/icons/AccountCircle";
import LoginModal from "../components/LoginModal";
export default function AccountNotRegistered() {
const [anchorEl, setAnchorEl] = React.useState(null);
const isMenuOpen = Boolean(anchorEl);
const handleProfileMenuOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const handleProfileMenuClose = () => {
setAnchorEl(null);
};
const menuId = "account-menu";
const renderMenu = (
<Menu
anchorEl={anchorEl}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
id={menuId}
keepMounted
transformOrigin={{ vertical: "top", horizontal: "right" }}
open={isMenuOpen}
onClose={handleProfileMenuClose}
>
<MenuItem onClick={handleLoginOpen}>Login</MenuItem> <------------------- HERE I CALL THE METHOD
<MenuItem onClick={handleProfileMenuClose}>Register</MenuItem>
</Menu>
);
return (
<div>
<IconButton color="inherit" onClick={handleProfileMenuOpen}>
<AccountCircleIcon />
</IconButton>
<LoginModal />
{renderMenu}
</div>
);
}
LoginModal.js:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Modal from "#material-ui/core/Modal";
import { Backdrop } from "#material-ui/core";
import Fade from "#material-ui/core/Fade";
const useStyles = makeStyles((theme) => ({
modal: {
display: "flex",
alignItems: "center",
justifyContent: "center",
},
paper: {
backgroundColor: theme.palette.background.paper,
border: "2px solid #000",
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
export default function LoginModal() {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleLoginOpen = () => { <----------------------------------HERE IS THE METHOD
setOpen(true);
};
const handleLoginClose = () => {
setOpen(false);
};
return (
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleLoginClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<div className={classes.paper}>
<h2 id="transition-modal-title">Transition modal</h2>
<p id="transition-modal-description">
react-transition-group animates me.
</p>
</div>
</Fade>
</Modal>
);
}
Thank you for your help!
If you want to use a method of another component you need to create parent-child relation of components and then pass those methods as a prop to child component.
You should use/call <LoginModal /> inside File1.js where this LoginModel would take a prop value for visibility of modal such as isModalShown and then a function to close the modal such as closeModal
so when you call the component it would look like:
<LoginModal isModalShown={isModalShown} closeModal={closeModal} />
You need to maintain the state variable in File1.js since showing/hiding modal is done in the same file.
import React from "react";
import { IconButton, Menu, MenuItem } from "#material-ui/core";
import AccountCircleIcon from "#material-ui/icons/AccountCircle";
import LoginModal from "../components/LoginModal";
export default function AccountNotRegistered() {
const [anchorEl, setAnchorEl] = React.useState(null);
const [isModalShown, toggleModal] = React.useState(false);
const closeModal = () => toggleModal(false)
const openModal = () => toggleModal(false)
const isMenuOpen = Boolean(anchorEl);
const handleProfileMenuOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const handleProfileMenuClose = () => {
setAnchorEl(null);
};
const menuId = "account-menu";
const renderMenu = (
<React.Fragment>
<Menu
anchorEl={anchorEl}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
id={menuId}
keepMounted
transformOrigin={{ vertical: "top", horizontal: "right" }}
open={isMenuOpen}
onClose={handleProfileMenuClose}>
<MenuItem onClick={openModal}>Login</MenuItem>
<MenuItem onClick={handleProfileMenuClose}>Register</MenuItem>
</Menu>
</React.Fragment>
);
return (
<div>
<IconButton color="inherit" onClick={handleProfileMenuOpen}>
<AccountCircleIcon />
</IconButton>
<LoginModal isModalShown={isModalShown} closeModal={closeModal} />
{renderMenu}
</div>
);
}
and then in the LoginModal.js
export default function LoginModal(props) {
const classes = useStyles();
return (
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={props.isModalShown}
onClose={props.closeModal}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<div className={classes.paper}>
<h2 id="transition-modal-title">Transition modal</h2>
<p id="transition-modal-description">
react-transition-group animates me.
</p>
</div>
</Fade>
</Modal>
);
}
I believe you want to show the modal on click of Register as well, you can create a wrapper for this Modal component and then pass custom props to it, which would then takes care of rendering <LoginModal /> or <RegisterModal />

Converting class to function React Error invalid hook call

I have been converting my class components to functions but I'm stuck on this hook error to do with my export default. I'm sure it's something simple, but I can't find the answer I'm looking for.
This is the code causing the error:
import React from 'react'
import {AppBar, Toolbar, Button, Typography, makeStyles} from '#material-ui/core'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import Menu from './Menu'
const useStyles = makeStyles((theme) => ({
header: {
backgroundColor: "#1d3834",
},
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
}))
function Header(props) {
const classes = useStyles()
const renderContent = () => {
switch (props.auth) {
case null:
return
case false:
return (
<Button color="inherit" href="/signin">Sign In</Button>
)
default:
return (
<Button color="inherit" href="/api/logout">Sign Out</Button>
)
}
}
return(
<div className={classes.root}>
<AppBar position="static" className={classes.header}>
<Toolbar>
<Menu/>
<Typography variant="h6" className={classes.title} >
<Link
to={props.auth ? '/items' : '/'}
className="left brand-logo"
>
</Link>
</Typography>
{renderContent()}
</Toolbar>
</AppBar>
</div>
);
}
function mapStateToProps({auth}) {
return{ auth }
}
export default connect(mapStateToProps)(makeStyles(useStyles) (Header))
I'm hoping someone has ran into a similar issue before and would be able to give me some feedback, Thanks :)
The main issue is how you export your component. Try instead as the following:
export default connect(mapStateToProps)(Header)
You don't really need the makeStyles(useStyles) part there.
+1 suggestion - not related to the question:
This suggestion is not related to the question itself, it's just a small improvement how I like to organize makeStyles stuff in my code repository with Material-UI.
Usually I create a styles.tsx which would look like in your case as the following, placed next to your component file:
import { makeStyles } from "#material-ui/core"
const useStyles = makeStyles((theme) => ({
header: {
backgroundColor: "#1d3834",
},
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
}))
export default useStyles
Then import in the component as the following:
import useStyles from "./styles"
And the usage similarly as in your component:
function Header(props) {
const classes = useStyles()
// ... rest of your component
}

Categories

Resources