I want to show message in material.ui by only call method not ading component to parent component (like toastify.js). So, I wrote example like below. But I couldn't call showSnack() method. How can I achieve this?
Note: I don't want add component to demo js like < SnackbarHelper />. I only want show snackbar calling by function.
CODESANDBOX LINK
Demo.js
import React from "react";
import Button from "#material-ui/core/Button";
import SnackHelper from "./snackHelper";
export default function PositionedSnackbar() {
function showMessage() {
console.log("I want call snackHelper.showSnack");
// snackHelper.showSnack();
}
return (
<div>
<Button variant="contained" onClick={() => showMessage()}>
SHOW MESSAGE
</Button>
</div>
);
}
snackbarHelper.js
import React from "react";
import Snackbar from "#material-ui/core/Snackbar";
export default function SnackHelper() {
const [state, setState] = React.useState({
open: false
});
const { vertical, horizontal, open } = state;
const showSnack = (newState) => () => {
setState({ open: true, ...newState });
};
const handleClose = () => {
setState({ ...state, open: false });
};
return (
<div>
<Snackbar
anchorOrigin={{ vertical, horizontal }}
open={open}
onClose={handleClose}
message=""
key={vertical + horizontal}
/>
</div>
);
}
I found solution in this article for same thing what I was looking. Only difference is, this is for confirmation dialog and written by typescript. But, it can be easily changed to toast message by javascript. https://dev.to/dmtrkovalenko/the-neatest-way-to-handle-alert-dialogs-in-react-1aoe
You can get working example code https://codesandbox.io/s/neat-dialogs-3h5ou?from-embed=&file=/src/ConfirmationService.tsx
Related
I'm trying to make a modal in React TypeScript and I can't figure out the exact type of a reference of a HTML dialog.
import { useRef } from 'react'
const Modal: React.FC = () => {
const modalRef = useRef<any>(null)
function openModal() {
modalRef.current.showModal()
}
function closeModal() {
modalRef.current.close()
}
return (
<div>
<button onClick={openModal}>Open<button/>
<dialog ref={modalRef}>
<h1>Title</h1>
<p>Info</p>
<button onClick={closeModal}>Close<button/>
</dialog>
<div/>
)
}
export default Modal
Could anyone help me figure out the right type I would put in place of any?
Most html components are just HTML${componentName}Element,
for example HTMLDivElement and in your case HTMLDialogElement
const modalRef = useRef<HTMLDialogElement>(null)
When I run npm start in chrome (create-react-app) it takes a while to load and then it crashes. the Error displays "out of memory". I cleared all my tabs and cache, and since other apps run just fine I have been able to tell the error is in the code. When I removed the Reserve_Button Component the application ran just fine. I think there may be some sort of memory leak related to SetState but I'm relatively new to React so there is not much more I can tell.
import React from "react";
import "./reserve_button.css";
// import Reserve_Form from "./Reserve_Form";
// import { connect } from "react-redux";
// import { submitreservation } from "../../Actions/index";
import Modal from "./modal";
class Button extends React.Component {
state = {
firstname: "",
Lastname: "",
};
showmodal = {
open: false,
};
// onSubmit = (formValues) => {
// this.props.submitreservation(formValues);
// };
showModal() {
if (this.state.open) {
return (
<Modal
open={this.state.open}
onDismiss={() => this.setState({ open: false })}
title="Modal Title"
content="Modal Body"
actions={<div className="ui button">Button</div>}
/>
);
}
}
render() {
return (
<div className="body">
<Button
onClick={() => this.setState({ open: !this.state.open })}
className="neon-button"
>
Reserve session
</Button>
{this.showModal()}
</div>
);
}
}
// <Reserve_Form onSubmit={this.onSubmit} />;
// export default connect(null, { submitreservation })(Button);
export default Button;
this is the Modal:
import React from "react";
import ReactDOM from "react-dom";
const Modal = (props) => {
return ReactDOM.createPortal(
<div onClick={props.onDismiss}>
<div onClick={(e) => e.stopPropagation()}>
<i onClick={props.onDismiss}></i>
<div className="header">{props.title}</div>
<div className="content">{props.content}</div>
<div className="actions">{props.actions}</div>
</div>
</div>,
document.querySelector("section")
);
};
export default Modal;
there are no set class names in a CSS file for the modal but I don't think that CSS would be the source of the problem.
UPDATE: when I console log this.state.open it prints "false" like 1000 times a second
There is an optimization you can make to your code. Perhaps it will help fix the error:
Instead of
<Button
onClick={() => this.setState({ open: !this.state.open })}
className="neon-button"
>
when relying on the previous state of a react component use the previous state to update the value. It looks something like this:
<Button
onClick={() => this.setState((prevState) => { open: !prevState.open })}
className="neon-button"
>
By doing this, we are referencing the previous state of this.state.open. This is particularly important as not all state updates happen instantaneously and so there is a possibility of referencing an older value of this.state.open in your approach.
I'm trying to anchor a popover component to a button component. The problem is that this doesn't seem to work if the button is styled using styled() (I'm using emotion).
This code causes the following warning: MUI: The `anchorEl` prop provided to the component is invalid.
Because anchorEl is invalid the popover will simply postion itself on the top left corner of the screen.
import { useState } from "react";
import { styled } from "#mui/material/styles";
import Popover from "#mui/material/Popover";
import Button from "#mui/material/Button";
export default function BasicPopover() {
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const StyledButton = styled((props) => <Button {...props} />)(
({ theme }) => ({
//some styling
})
);
return (
<div>
<StyledButton
variant="contained"
onClick={handleClick}
>
Open Popover
</StyledButton>
<Popover open={open} anchorEl={anchorEl} onClose={handleClose}>
The content of the Popover.
</Popover>
</div>
);
}
I found a slightly different approach using refs here, but I couldn't figure out how to make it work with styled() either.
I'm still rather new to react so please be gentle.
I don't know why, but if you move the styled() out of the main component it works.
const StyledButton = styled((props) => <Button {...props} />)(
({ theme }) => ({
//some styling
})
);
export default function BasicPopover() {
//[...]
}
I am trying to use the react.js spinner component and I can't figure out how I to hide it once my task is complete.
Here is a simple codepen where I am using hidden property. I will set it up to false or true depending on if I want to show it or not:
https://codepen.io/manish_shukla01/pen/GLNOyw
<Spinner hidden={true} size={SpinnerSize.large} label="manish's large spinned" />
You need to use conditional rendering to hide/show the spinner. You can define a component state then can set it up to false or true depending on if you want to show it or not.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden: false
};
}
render() {
return (
<div className="App">
{!this.state.hidden && <SpinnerBasicExample />}
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
For more you can read this https://reactjs.org/docs/conditional-rendering.html
Conditional rendering using state.
In React, you can create distinct components that encapsulate behavior you need. Then, you can render only some of them, depending on the state of your application.
Working example (click on the Dashboard tab):
containers/Dashboard
import isEmpty from "lodash/isEmpty";
import React, { Component } from "react";
import api from "../../utils/api";
import DisplayUser from "../../components/DisplayUser";
import DisplaySignUp from "../../components/DisplaySignUp";
import Spinner from "../../components/Spinner";
class Dashboard extends Component {
state = {
isLoading: true,
currentUser: {}
};
componentDidMount = () => {
this.fetchUsers();
};
fetchUsers = async () => {
try {
const res = await api.get(`/users/1`);
setTimeout(() => {
this.setState({ currentUser: res.data, isLoading: false });
}, 1500);
} catch (e) {
console.error(e.toString());
}
};
// the below can be read like so:
// if "isLoading" is true... then display a spinner
// else if "currentUser" is not empty... then display the user details
// else show a signup message
render = () =>
this.state.isLoading ? (
<Spinner />
) : !isEmpty(this.state.currentUser) ? (
<DisplayUser currentUser={this.state.currentUser} />
) : (
<DisplaySignUp />
);
}
export default Dashboard;
For what you intend to do, just adding the hidden prop won't work as that is not an expected attribute of the Spinner component. I think what you need to do is to introduce conditional rendering in your component. Kindly see implementation below:
import * as React from 'react';
import { render } from 'react-dom';
import {
PrimaryButton,
Spinner,
SpinnerSize,
Label,
IStackProps,
Stack
} from 'office-ui-fabric-react';
import './styles.css';
const { useState } = React;
const SpinnerBasicExample: React.StatelessComponent = () => {
// This is just for laying out the label and spinner (spinners don't have to be inside a Stack)
const rowProps: IStackProps = { horizontal: true, verticalAlign: 'center' };
const tokens = {
sectionStack: {
childrenGap: 10
},
spinnerStack: {
childrenGap: 20
}
};
return (
<Stack tokens={tokens.sectionStack}>
<Stack {...rowProps} tokens={tokens.spinnerStack}>
<Label>Extra small spinner</Label>
<Spinner size={SpinnerSize.xSmall} />
</Stack>
<Stack {...rowProps} tokens={tokens.spinnerStack}>
<Label>Small spinner</Label>
<Spinner size={SpinnerSize.small} />
</Stack>
</Stack>
);
};
function App() {
const [hidden, setHidden] = useState(false);
return (
<div className="App">
{hidden && <SpinnerBasicExample />}
<PrimaryButton
data-automation-id="test"
text={!hidden ? 'Show spinner' : 'Hide spinner'}
onClick={() => setHidden(!hidden)}
allowDisabledFocus={true}
/>
</div>
);
}
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>