React UseState keeps calling API over and over - rest

UseEffect keeps calling my api over and over again. If I keep everything the same and replace the axios call with just a simple console.log, though, UseEffect only completes once. I am including only one link on the navbar because that is all that is needed to create the problem.
Here is my router:
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import { Navbar, Nav} from 'react-bootstrap';
import Home from './Home.js';
import React from 'react';
export default function Routing(props) {
return (
<>
<Router>
<Navbar collapseOnSelect className="navbar">
<div className="h-box">
<Navbar.Brand>
<h1>Portland Watershed Data Dashboard</h1>
</Navbar.Brand>
</div>
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ml-auto align-items-center">
<Link to="/"><h2 className="big link">Home</h2></Link>
</Nav>
</Navbar.Collapse>
</Navbar>
<Switch>
<>
<Route exact path="/">
<Home />
</Route>
<Route exact path="/home">
<Home />
</Route>
</>
</Switch>
</Router>
</>
)
}
and here is the api call on the Home component:
import { React, useEffect, useState } from "react";
import axios from 'axios';
export default function Home(props) {
const [discharge, setDischargeData] = useState(null);
useEffect(() => {
getDischargeData();
async function getDischargeData() {
const response = await axios.get("https://waterservices.usgs.gov/nwis/dv/?format=json&indent=on&parameterCd=00060&statCd=00003&sites=14211820,%2014144700,%2014211315,%2014206900,%2014211550"
);
setDischargeData(response.data);
console.log(response.data);
}
}, [discharge])
return (
<>
<p>test</p>
</>
)
}

You have an infinite loop:
Remove the parameter "discharge" from the brackets in your callback.
useEffect(() => {
getDischargeData();
async function getDischargeData() {
const response = await axios.get("https://waterservices.usgs.gov/nwis/dv/?format=json&indent=on&parameterCd=00060&statCd=00003&sites=14211820,%2014144700,%2014211315,%2014206900,%2014211550"
);
setDischargeData(response.data);
console.log(response.data);
}
}, [])
^

Related

Cannot override default Material UI Icon styling

I have an AccountCircleIcon Material UI icon and I want to increase the size, I've tried several things:
import AccountCircleIcon from '#material-ui/icons/AccountCircle';
import {withStyles} from '#material-ui/styles';
const StyledIcon = withStyles({
root: {
fontSize: '50rem',
},
})(AccountCircleIcon);
const login = () => {
return (
<div><StyledIcon /></div>
);
};
import AccountCircleIcon from '#material-ui/icons/AccountCircle';
import {makeStyles} from '#material-ui/styles';
const useStyles = makeStyles({
root: {
fontSize: '50rem',
},
});
const login = () => {
const classes = useStyles();
return (
<div><AccountCircleIcon classes={{root: classes.root}} /></div>
);
};
import AccountCircleIcon from '#material-ui/icons/AccountCircle';
import {makeStyles} from '#material-ui/styles';
const useStyles = makeStyles({
avatarIcon: {
fontSize: '50rem',
},
});
const login = () => {
const classes = useStyles();
return (
<div><AccountCircleIcon className={classes.avatarIcon} /></div>
);
};
But each time the default icon styling overrides the added styling:
I missed the StyledEngineProvider https://mui.com/guides/interoperability/ wrapping that around your app assures the custom css is injected first:
import * as React from 'react';
import { StyledEngineProvider } from '#mui/material/styles';
export default function GlobalCssPriority() {
return (
<StyledEngineProvider injectFirst>
{/* Your component tree. Now you can override MUI's styles. */}
</StyledEngineProvider>
);
}
https://codesandbox.io/s/laughing-alex-nyszr?file=/src/Demo.tsx

Next.js with MUI v4 styles flicker

First of all, sorry for my English.
I tried to setup correctly MUI v4 for Next.js but I’ve always styles flicker.
I’ve :
Next: 10.0.5
Material-ui/core: 4.9.14
And I use Material-UI's styling solution.
No solution found on the web can’t correct this behavior for now.
In my _app.js :
import { useEffect } from 'react';
import { ThemeProvider } from '#material-ui/core/styles';
import CssBaseline from '#material-ui/core/CssBaseline';
import { AuthProvider } from '#lib/useAuth';
import DefaultLayout from '#layouts/DefaultLayout';
import theme from '#assets/styles/jss/theme';
import '#assets/styles/scss/toastify.scss';
const MyApp = ({ Component, pageProps }) => {
const Layout = Component.Layout || DefaultLayout;
useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<ThemeProvider theme={theme}>
<AuthProvider>
<CssBaseline />
<Layout>
<Component {...pageProps}> </Component>
</Layout>
</AuthProvider>
</ThemeProvider>
);
};
export default MyApp;
In my _document.js :
import React from 'react';
import Document, {
Html,
Head,
Main,
NextScript,
} from 'next/document';
import { ServerStyleSheets } from '#material-ui/core/styles';
class MyDocument extends Document {
render() {
return (
<Html lang="fr">
<Head>
<link rel="icon" href="/favicon.ico" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
MyDocument.getInitialProps = async (ctx) => {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () => originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()],
};
};
export default MyDocument;
For example, in navbar component I’m doing :
import { makeStyles } from '#material-ui/core/styles';
import styles from '#assets/styles/jss/components/navbarStyle';
const useStyles = makeStyles(styles, { name: 'MuiCustomNavbar' });
const Navbar = () => {
const classes = useStyles();
[…]
}
But i’v always styles flicker in production when I reload page.
Anyone can help me please ?
Thanks !

Tooltip with content in Material-UI

I have following tooltip:
export const EstimateTableActions = () => {
return (
<>
<Tooltip title="Actions" aria-label="add">
<Fab color="primary">
<MoreVertIcon />
</Fab>
</Tooltip>
</>
);
};
which looks like:
And I want to add content to the tooltip, I want to have some div with my actions after click, in the documentation there is no any example on that, any idea?
You can use speed dial component to add actions of your choice --
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Backdrop from '#material-ui/core/Backdrop';
import SpeedDial from '#material-ui/lab/SpeedDial';
import SpeedDialIcon from '#material-ui/lab/SpeedDialIcon';
import SpeedDialAction from '#material-ui/lab/SpeedDialAction';
import FileCopyIcon from '#material-ui/icons/FileCopyOutlined';
import SaveIcon from '#material-ui/icons/Save';
import PrintIcon from '#material-ui/icons/Print';
import ShareIcon from '#material-ui/icons/Share';
import FavoriteIcon from '#material-ui/icons/Favorite';
const useStyles = makeStyles((theme) => ({
root: {
height: 380,
transform: 'translateZ(0px)',
flexGrow: 1,
},
speedDial: {
position: 'absolute',
bottom: theme.spacing(2),
right: theme.spacing(2),
},
}));
const actions = [
{ icon: <FileCopyIcon />, name: 'Copy' },
{ icon: <SaveIcon />, name: 'Save' },
{ icon: <PrintIcon />, name: 'Print' },
{ icon: <ShareIcon />, name: 'Share' },
{ icon: <FavoriteIcon />, name: 'Like' },
];
export default function SpeedDialTooltipOpen() {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const [hidden, setHidden] = React.useState(false);
const handleVisibility = () => {
setHidden((prevHidden) => !prevHidden);
};
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div className={classes.root}>
<Button onClick={handleVisibility}>Toggle Speed Dial</Button>
<Backdrop open={open} />
<SpeedDial
ariaLabel="SpeedDial tooltip example"
className={classes.speedDial}
hidden={hidden}
icon={<SpeedDialIcon />}
onClose={handleClose}
onOpen={handleOpen}
open={true}
>
{actions.map((action) => (
<SpeedDialAction
key={action.name}
icon={action.icon}
tooltipTitle={action.name}
tooltipOpen
onClick={handleClose}
/>
))}
</SpeedDial>
</div>
);
}
Working sandbox here - https://codesandbox.io/s/qogpp

IconButton with label

Is there something I can use in/with IconButton to define a label below the icon?
Maybe something similar to BottomNavigationAction but without it having to be inside of a BottomNavigation.
You can add your label as a direct child of the IconButton (sibling of the Icon itself), and override the IconButton-label style to have flexDirection: column
import React from 'react';
import {IconButton} from '#material-ui/core';
import { makeStyles } from '#material-ui/core/styles';
import SaveIcon from '#material-ui/icons/Save';
const useStyles = makeStyles(theme => ({
iconButtonLabel: {
display: 'flex',
flexDirection: 'column',
},
}));
export default function IconButtonWithLabel() {
const classes = useStyles();
return (
<IconButton classes={{label: classes.iconButtonLabel}}>
<SaveIcon/>
<div>
hello
</div>
</IconButton>
);
}

Clipped drawer in Material ui

According to docs, material-ui supports persistant drawer.
But my expected behaviour is a clipped persistant drawer like the photo.
My Sidebar component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import Drawer from 'material-ui/Drawer';
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List';
import Face from 'material-ui-icons/Face';
import Person from 'material-ui-icons/Person';
import Assignment from 'material-ui-icons/Assignment';
import NavLink from 'react-router-dom/NavLink';
import { FormattedMessage } from 'react-intl';
import styles from '../../../../style/components/global/Sidebar.scss';
const cx = require('classnames/bind').bind(styles);
const rootStyles = theme => ({
list: {
width: 250,
flex: 'initial',
},
drawer: {
top: 30,
},
});
class UndockedDrawer extends Component {
render() {
const { classes } = this.props;
const sidebarListItems = (
<div>
<NavLink
to="/users"
className={cx('noStyle')}
>
<ListItem button>
<ListItemIcon>
<Person />
</ListItemIcon>
<ListItemText primary={<FormattedMessage id="user" />} />
</ListItem>
</NavLink>
</div>
);
const sidebarList = (
<div>
<List className={classes.list}>
{sidebarListItems}
</List>
</div>
);
return (
<div>
<Drawer
open={this.props.open}
onRequestClose={this.props.onRequestClose}
onClick={this.props.onRequestClose()}
type="permanent">
{sidebarList}
</Drawer>
</div>
);
}
}
export default withStyles(rootStyles)(UndockedDrawer);
So far, I've tried to make top property as much as AppBar's height but this behaviour wasn't what I needed.
Is there any way to achieve this?
You need to provide the right styles for the AppBar. Taking the example from the docs you provided:
Instead of:
const styles = theme => ({
...
appBar: {
position: 'absolute',
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth,
},
...
});
Use:
const styles = theme => ({
...
appBar: {
position: 'absolute',
width: '100%',
zIndex: '1400',
},
...
});
Why zIndex is 1400? It's just an arbitrary number that is higher than the zIndex of the Drawer, which is 1300.