I created a Snackbar component.
import React, { Component } from 'react';
import { Snackbar, IconButton } from '#material-ui/core';
import CloseIcon from '#material-ui/icons/Close';
interface AlertProps {
message: string;
}
interface AlertState {
open: boolean;
}
export default class Alert extends Component<AlertProps, AlertState> {
constructor(props: AlertProps) {
super(props);
this.state = {
open: true
};
this.handleClose = this.handleClose.bind(this);
}
handleClose(event: React.SyntheticEvent | React.MouseEvent, reason?: string) {
if (reason !== 'clickaway') {
this.setState({
open: false
});
}
}
render() {
return (
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={this.state.open}
autoHideDuration={6000}
onClose={this.handleClose}
message={this.props.message}
action={
<IconButton
key="close"
color="inherit"
onClick={this.handleClose}
>
<CloseIcon />
</IconButton>
}
/>
)
}
}
I then programmatically add it to a render when an error is encountered while submitting a form.
let alert: ReactNode;
if (this.state.error) {
alert = <Alert message={this.state.error} />;
}
Problem is the Snackbar only opens the first time an error is encountered. If a user submits the same form twice, the Snackbar doesn’t open.
I know it’s because of this.state.open = false which is set by the onClose method, but how can I "reset" this state before the form is submitted again?
One way is you could change your approach a bit and always have Alert rendered, i.e.
<Alert message={this.state.error} open={this.state.open} onClose={()=>{this.setState({open:false})}}/>
Also move the open state variable from Alert's state to its parent. So in Alert use the open value from props always. Now, whenever open is changed in the parent, Alert will re render properly.
Related
I got following warning, but I do not know which data change it means, do you have any idea?
Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See react-unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at:
* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.
Please update the following components: t
code:
import React, { Component } from "react";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import Button from "#material-ui/core/Button";
import FacebookLogin from "react-facebook-login";
import Menu from "#material-ui/core/Menu";
import MenuItem from "#material-ui/core/MenuItem";
// import this
import { withStyles } from "#material-ui/core/styles";
// make this
const styles = theme => ({
root: {
flexGrow: 1
},
menuButton: {
marginRight: theme.spacing(2)
},
title: {
flexGrow: 1
}
});
class App extends Component {
state = {
accessToken: "",
isLoggedIn: false,
userID: "",
name: "",
email: "",
picture: ""
};
responseFacebook = response => {
this.setState({
accessToken: response.accessToken,
isLoggedIn: true,
userID: response.userID,
name: response.name,
email: response.email,
picture: response.picture.data.url
});
};
handleClick = event => this.setState({ anchorEl: event.currentTarget });
handleClose = () => {
this.setState({ anchorEl: undefined });
};
handleCloseAndLogOut = () => {
this.setState({ anchorEl: undefined });
this.setState({ isLoggedIn: undefined });
this.setState({ userID: undefined });
this.setState({ name: undefined });
this.setState({ email: undefined });
this.setState({ picture: undefined });
};
componentDidMount() {
document.title = "Tiket.hu";
}
componentDidUpdate() {
// What put here?
}
render() {
let fbContent;
let listContent;
//const { anchorEl } = this.state;
if (this.state.isLoggedIn) {
fbContent = (
<div>
<Button
aria-controls="simple-menu"
aria-haspopup="true"
onClick={this.handleClick}
>
{this.state.name}
</Button>
<Menu
id="simple-menu"
anchorEl={this.status.anchorEl}
keepMounted
open={Boolean(this.status.anchorEl)}
onClose={this.handleClose}
>
<MenuItem onClick={this.handleCloseAndLogOut}>Log out</MenuItem>
<MenuItem onClick={this.handleClose}>
Switch mode to Release
</MenuItem>
<MenuItem onClick={this.handleClose}>My tickets</MenuItem>
</Menu>
</div>
);
} else {
let fbAppId;
if (
window.location.hostname === "localhost" ||
window.location.hostname === "127.0.0.1"
)
fbAppId = "402670860613108";
else fbAppId = "2526636684068727";
fbContent = (
<FacebookLogin
appId={fbAppId}
autoLoad={true}
fields="name,email,picture"
onClick={this.componentClicked}
callback={this.responseFacebook}
/>
);
}
return (
<div className="App">
<AppBar position="static">
<Toolbar>
<Typography variant="h6" className={this.props.classes.title}>
Tiket.hu
</Typography>
<Button color="inherit">Search</Button>
<Button color="inherit">Basket</Button>
{fbContent}
</Toolbar>
</AppBar>
{listContent}
</div>
);
}
}
export default withStyles(styles)(App);
The component you show doesn't have a componentWillReceiveProps, so it's not the problem. The error message includes this:
Please update the following components: t
So we're looking for a component named t. If you have one with that name, that's the one you'd need to fix. But my guess is that's not one of your components, but rather one that's being used indirectly by one of the libraries you've imported. If that's the case, only the library creator will be able to fix this, and you'd then import a new version of their code.
This warning will not stop your code from running with current versions of react, but it will prevent you from upgrading to version 17 of react (which doesn't exist yet)
I have AppBar component with a menu. Every time I click on any of the links, navigation happens, but the menu doesn't close.
import MenuItem from '#material-ui/core/MenuItem';
import Menu from '#material-ui/core/Menu';
import { Button } from '#material-ui/core';
import * as ROUTES from '../../constants/routes';
class AppBar extends React.Component {
state = {
anchorEl: null
};
handleCommonMenuOpen = event => {
this.setState({ anchorEl: event.currentTarget });
}
handleCommonMenuClose = () => {
this.setState({ anchorEl: null });
};
render() {
const { anchorEl } = this.state;
const { classes } = this.props;
const isMenuOpen = Boolean(anchorEl);
const renderCommonMenu = (<Menu
anchorEl={anchorEl}
id="tutorials-menu"
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
open={isMenuOpen}
onClose={this.handleCommonMenuClose}
>
<MenuItem onClick={this.handleCommonMenuClose} component={Link} to={ROUTES.AUTHENTICATION_DOC}>Authentication</MenuItem>
<MenuItem onClick={this.handleCommonMenuClose} component={Link} to={ROUTES.REALTIMEDB_DOC}>Realtime DB</MenuItem>
<MenuItem onClick={this.handleCommonMenuClose} component={Link} to={ROUTES.HOSTING_DOC}>Hosting</MenuItem>
</Menu>)
return(
<div>
<Button aria-haspopup="true" aria-controls="tutorials-menu" onClick={this.handleCommonMenuOpen} color="inherit">Tutorials</Button>
{renderCommonMenu}
<Route path={ROUTES.AUTHENTICATION_DOC} component={AuthenticationDoc} />
<Route path={ROUTES.REALTIMEDB_DOC} component={ReattimeDBDoc} />
</div>)
}
}
export default AppBar;
If I click the handleCommonMenuClose, it will result in an error.
"cannot convert undefined or null to object"
Could anybody help me out to resolve this issue, please?
I am using social login in react-admin (former admin-on-rest) and I have the user picture from his social media, however I didn't find how to change the user profile image in the top right corner of the screen:
Is there any prop to be set, like custom login or custom logout button?
Thanks.
Currently, the process involves a lot of code as you'll have to rewrite the UserMenu completely. To use it, you'll also have to implement a custom Layout with a custom AppBar. The process will be simplified when https://github.com/marmelab/react-admin/pull/2391 will be merged.
// in src/MyUserMenu.js
import React, { Children, cloneElement } from 'react';
import PropTypes from 'prop-types';
import Tooltip from '#material-ui/core/Tooltip';
import IconButton from '#material-ui/core/IconButton';
import Menu from '#material-ui/core/Menu';
import AccountCircle from '#material-ui/icons/AccountCircle';
import { translate } from 'ra-core';
class UserMenu extends React.Component {
static propTypes = {
children: PropTypes.node,
label: PropTypes.string.isRequired,
logout: PropTypes.node,
translate: PropTypes.func.isRequired,
};
static defaultProps = {
label: 'ra.auth.user_menu',
};
state = {
auth: true,
anchorEl: null,
};
handleChange = (event, checked) => {
this.setState({ auth: checked });
};
handleMenu = event => {
this.setState({ anchorEl: event.currentTarget });
};
handleClose = () => {
this.setState({ anchorEl: null });
};
render() {
const { children, label, logout, translate } = this.props;
if (!logout && !children) return null;
const { anchorEl } = this.state;
const open = Boolean(anchorEl);
return (
<div>
<Tooltip title={label && translate(label, { _: label })}>
<IconButton
arial-label={label && translate(label, { _: label })}
aria-owns={open ? 'menu-appbar' : null}
aria-haspopup="true"
onClick={this.handleMenu}
color="inherit"
>
{/* Replace this icon with whatever you want, a user avatar or another icon */}
<AccountCircle />
</IconButton>
</Tooltip>
<Menu
id="menu-appbar"
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={open}
onClose={this.handleClose}
>
{Children.map(children, menuItem =>
cloneElement(menuItem, { onClick: this.handleClose })
)}
{logout}
</Menu>
</div>
);
}
}
export default translate(UserMenu);
// in src/MyAppBar.js
import { AppBar } from 'react-admin';
import MyUserMenu from './MyUserMenu';
const MyAppBar = (props) => <AppBar {...props} userMenu={MyUserMenu} />;
// in src/MyLayout.js
import { Layout } from 'react-admin';
import MyAppBar from './MyAppBar';
const MyLayout = (props) => <Layout {...props} appBar={MyAppBar} />;
export default MyLayout;
Documentation: https://marmelab.com/react-admin/Theming.html#using-a-custom-appbar
I am using react with MUI framework and I was wondering how can I create an loading button using this framework?
I am looking for something similar to this.
To the best of my knowledge, there is no single component that accomplishes this out of the box in material-ui. However, you can implement your own easily using CircularProgress.
Assuming you are using material-ui v1, here's a rough example. First, I create a LoadingButton that accepts a loading prop - if that prop is true, I display a CircularProgress indicator. It also accepts a done prop - if that's true, the button clears the progress indicator and becomes a checkmark to show success.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import Button from 'material-ui/Button';
import { CircularProgress } from 'material-ui/Progress';
import Check from 'material-ui-icons/Check';
const styles = theme => ({
button: {
margin: theme.spacing.unit,
},
});
const LoadingButton = (props) => {
const { classes, loading, done, ...other } = props;
if (done) {
return (
<Button className={classes.button} {...other} disabled>
<Check />
</Button>
);
}
else if (loading) {
return (
<Button className={classes.button} {...other}>
<CircularProgress />
</Button>
);
} else {
return (
<Button className={classes.button} {...other} />
);
}
}
LoadingButton.defaultProps = {
loading: false,
done: false,
};
LoadingButton.propTypes = {
classes: PropTypes.object.isRequired,
loading: PropTypes.bool,
done: PropTypes.bool,
};
export default withStyles(styles)(LoadingButton);
You can use the LoadingButton as shown in the following example, which uses state to set the appropriate prop on the button.
import React from 'react';
import LoadingButton from './LoadingButton';
class ControlledButton extends React.Component {
constructor(props) {
super(props);
this.state = { loading: false, finished: false };
}
render() {
const { loading, finished } = this.state;
const setLoading = !finished && loading;
return (
<div>
<LoadingButton
loading={setLoading}
done={finished}
onClick={() => {
// Clicked, so show the progress dialog
this.setState({ loading: true });
// In a 1.5 seconds, end the progress to show that it's done
setTimeout(() => { this.setState({ finished: true })}, 1500);
}}
>
Click Me
</LoadingButton>
</div>
);
}
}
export default ControlledButton;
You can of course tweak the styling and functionality to meet your exact needs.
In the newer versions of MUI, you can use LoadingButton component, it's currently in the lab package. This is just a wrapper of the Button with a loading prop. You can customize the loadingIndicator component and its position. See the example below:
import LoadingButton from '#mui/lab/LoadingButton';
<LoadingButton loading={loading}>
Text
</LoadingButton>
<LoadingButton
endIcon={<SendIcon />}
loading={loading}
loadingPosition="end"
variant="contained"
>
Send
</LoadingButton>
i am using react + material-ui .
i created dialog component in jsx file like this:
export default class CartoviewAbout extends React.Component {
constructor(props) {
super(props);
this.state = {open: false};
}
_handleOpen() {
this.setState({open: true});
};
_handleClose() {
this.setState({open: false});
};
render() {
const actions = [
<FlatButton
label="Close"
primary={true}
keyboardFocused={true}
onTouchTap={this._handleClose.bind(this)}
/>,
];
return (
<div>
<MenuItem
onTouchTap={this._handleOpen.bind(this)}
primaryText="Show About Dialog"
/>
<Dialog
title={title}
actions={actions}
modal={false}
open={this.state.open}
onRequestClose={this._handleClose.bind(this)}
autoScrollBodyContent={true}
contentClassName="dialog"
bodyClassName="dialog_body"
>
<div ><p>{abstract}</p>
</div>
</Dialog>
</div>
);
}
}
and i use this component in menu in another file but then i click the menu item dialog open and menu not close:
export default class CartoviewAppBar extends React.Component {
constructor(props) {
super(props);
}
render() {
const about = appConfig.showAbout ? React.createElement(CartoviewAbout) : "";
const icon_menu = <IconMenu
iconButtonElement={<IconButton><MoreVertIcon /></IconButton>}
anchorOrigin={{horizontal: 'right', vertical: 'top'}}
targetOrigin={{horizontal: 'right', vertical: 'top'}}
>
{about}
</IconMenu>;
return (
<div>
<AppBar
title={''}
showMenuIconButton={false}
iconElementRight={icon_menu}
/>
</div>
);
}
image:
i want menu to close when dialog open
You'll need to add the Dialog component outside the IconMenu. The IconMenu's onRequestChange event wont fire until the dialog closes.
Try this may be a workaround.
_handleOpen() {
window.setTimeout(() => {
this.setState({open: true});
}, 100); //any arbitary timeout
};
MenuItem onClick will automatically trigger for closing IconMenu
I think opening Dialog suppressing the event for closing IconMenu. So opening Dialog after closing IconMenu using setTimeout