Add a function into a onClick - javascript

I want to use React.js to build a single page application and I want to create a list in a material-ui drawer. I want to add an element into an array every time I press a button but I don't how to write this function.
Here is my buttom:
<RaisedButton
label="Next"
primary={true}
onClick={this.onNext}
/>
Here is onNext function:
onNext = (event) => {
const current = this.state.controlledDate;
const date = current.add(1, 'days');
this.setState({
controlledDate: date
});
this.getImage(moment(date));
}
And this is the code I want to add into onNext function:
menuItems.push(<MenuItem onClick={this.handleClose}>{this.state.image.date}</MenuItem>);

This is a sample code that adds drawer menu items using state
const { RaisedButton, MuiThemeProvider, Drawer, getMuiTheme, MenuItem } = MaterialUI;
class Sample extends React.Component {
state = {
open: false,
items: [],
}
handleClose = () => {
this.setState({ open: false });
}
handleOpen = () => {
this.setState({ open: true })
}
onNext = () => {
this.setState(state => {
return Object.assign({}, state, {
items: state.items.concat([
{
// add any other button props here (date, image, etc.)
text: `Item ${state.items.length + 1}`
}
]),
});
})
}
render() {
return (
<div>
<Drawer
openSecondary={true}
width={200}
open={this.state.open}
>
{this.state.items.map(item => (
<MenuItem onClick={this.handleClose}>{item.text}</MenuItem>
))}
</Drawer>
<RaisedButton
label="Next"
primary={true}
style={{ margin: 12 }}
onClick={this.onNext} />
<RaisedButton
label="Open Drawer"
primary={true}
style={{ margin: 12 }}
onClick={this.handleOpen} />
</div>
);
}
}
const App = () => (
<MuiThemeProvider muiTheme={getMuiTheme()}>
<Sample />
</MuiThemeProvider>
);
ReactDOM.render(
<App />,
document.getElementById('container')
);
Try it here: https://jsfiddle.net/jprogd/eq533rzL/
Hope it should give you an idea how to go further

Related

How can I make the Snackbar work in a class component?

These are my codes for the snackbar and it wasn't working whenever I'll click the button. I wanted the snackbar to appear once I'll click the button "confirm". Almost all of the examples I have seen are in a functional component, so how can I make the Snackbar work as expected in a class component?
class name extends Component {
constructor() {
super();
this.state = { orders: [], open: false };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
columns = [
{
name: "Confirm",
options: {
customBodyRender: (value, tableMeta) => {
return (
<FormControlLabel
value={value}
control={
<Button>
confirm
</Button>
}
onClick={(e) => {
try {
//firestore codes
);
} catch (err) {
console.log(err);
}
this.handleOpen();
}}
/>
);
},
},
},
];
//code for options
//data fetching codes
render() {
const { open } = this.state;
return this.state.orders ? (
<div>
//muidatatable codes
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
open={open}
onClose={this.handleClose}
autoHideDuration={2000}
// other Snackbar props
>
Order Confirmed
</Snackbar>
</div>
) : (
<p>Loading...</p>
);
}
}
Ignoring a few syntax errors, you should check if there are any orders by using the length and not just by mere existence of the array as you have initialized an empty array this.state.orders will always result in true. Instead use this.state.orders.length > 0 ? to check if there are any orders or not.
Snackbar's child(ren) should be wrapped in components and not just strings directly, for using string directly you can use message prop of Snackbar.
Also, it's a standard to write class's name starting with an upper-case letter.
Here's a working code: Material UI Snackbar using classes
import React, { Component } from "react";
import { FormControlLabel, Button, Snackbar } from "#material-ui/core";
import MuiAlert from "#material-ui/lab/Alert";
export default class Name extends Component {
constructor() {
super();
this.state = { orders: [], open: false };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
handleClick = () => this.setState({ orders: [1], open: true });
columns = [
{
name: "Confirm",
options: {
customBodyRender: (value, tableMeta) => {
return (
<FormControlLabel
value={value}
control={<Button>confirm</Button>}
onClick={(e) => {
try {
//firestore codes
} catch (err) {
console.log(err);
}
this.handleOpen();
}}
/>
);
}
}
}
];
//code for options
//data fetching codes
render() {
const { open } = this.state;
return (
<>
<Button variant="outlined" onClick={this.handleClick}>
Open snackbar
</Button>
{this.state.orders.length > 0 ? (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
open={open}
onClose={this.handleClose}
autoHideDuration={2000}
// other Snackbar props
>
{/* <span
style={{
background: "#000",
color: "#fff",
padding: "20px 5px",
width: "100%",
borderRadius: "5px"
}}
>
Order Confirmed
</span> */}
<MuiAlert
onClose={this.handleClose}
severity="success"
elevation={6}
variant="filled"
>
Success Message
</MuiAlert>
</Snackbar>
</div>
) : (
<p>loading...</p>
)}
</>
);
}
}
The following changes are made to make it work:
Removed Order Confirmed and used message prop of Snackbar
Passed values to orders array in constructor
Passed true in open variable.
Below is the working code for snack bar.
import React, { Component } from "react";
import Snackbar from "#material-ui/core/Snackbar";
class SnackBarSof extends Component {
constructor() {
super();
this.state = { orders: [1, 2], open: true };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
render() {
console.log(this.state.orders);
console.log(this.state);
const { open } = this.state;
return this.state.orders ? (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
open={open}
onClose={this.handleClose}
message="order confirmed"
autoHideDuration={2000}
></Snackbar>
</div>
) : (
<p>Loading...</p>
);
}
}
export default SnackBarSof;

Opening Modal from different component

CardComponent:
export class Card extends Component<Prop, State> {
state = {
isCancelModalOpen: false,
};
marketService = new MarketService();
deleteMarket = () => {
this.marketService
.deleteMar()
.then((response) => {
})
.catch((error) => {
console.log(error);
});
};
handleModalToggle = () => {
this.setState(({ isCancelModalOpen }) => ({
isCancelModalOpen: !isCancelModalOpen,
}));
};
render() {
const {isCancelModalOpen, isDropdownOpen } = this.state;
const dropdownItems = [
<DropdownItem
key="action"
component="button"
onClick={this.handleModalToggle}
>
Delete
</DropdownItem>
];
return (
{this.CancelModal.map((listing) => (
<DeleteModal handleModal={this.handleModalToggle}
deleteProd = {this.deleteProduct}
description = {listing.description} ></DeleteModal>
))}
);
}
}
DeleteModal Component:
interface Prop {
description: string;
handleModal: Function;
deleteProd: Function;
}
class DeleteModal extends Component<Prop, State> {
state = {
deleteConfirmModel: false
};
render() {
const { deleteConfirmModel } = this.state;
const {description} = this.props;
return (
<Modal
isSmall
title="Confirmation"
isOpen={deleteConfirmModel}
onClose={() => this.props.handleModal}
showClose = {false}
actions={[
<Button
key="confirm"
variant="primary"
onClick={() => this.props.deleteProd}
>
Delete
</Button>,
<Button key="cancel" variant="link" onClick={() => this.props.handleModal}>
Cancel
</Button>
]}
>
{description}
</Modal>
);
}
}
export default DeleteModal;
I want DeleteModal component to get open on click of Dropdown Delete button available in Card component. There is no error in the code, still I am not able to trigger modal on click of Dropdown from Card component. Can anyone help me with what's wrong with the code?
So I found the solution to call the modal from different component:
In the card component: to call deleteModal component:
<DeleteModal
displayModal={deleteModalOpen}
handleModal={this.handleModalToggle}
description="Are you sure you want to delete"
/>
DisplayModal, handleModal,decription will be props in DeleteModal component:
DeleteModal:
interface Prop {
displayModal: boolean;
handleModal: Function;
description: string;
}
<Modal
isSmall
title="Confirmation"
isOpen={this.props.displayModal}
onClose={() => this.props.handleModal()}
showClose={false}
actions={[
<Button
key="cancel"
variant="link"
onClick={() => this.props.handleModal()}
>
Cancel
</Button>,
]}
>
{this.props.description}
</Modal>

Event Handler Not Setting State Before Component Renders

Component renders with initial state before state is updated.
The initial state is null and onHandlePrint method updates the state when the button is clicked.
class App extends React.Component {
state = {
pdf: null,
};
updatePDF = (data) => {
}
onHandlePrint = (pdf) => {
this.setState({pdf}, () => {
this.updatePDF(this.state.pdf)
})
}
render() {
return (
<div className="container">
<Router>
<ActivityDetail results={this.state.results} clickPrint={this.onHandlePrint} />
<Switch>
<Route
path="/pdf"
render={() => (
<PDFDocument data={this.state.pdf} />
)}
/>
</Switch>
</Router>
</div>
);
}
}
The button is a Link using to open a new tab that will render a PDF document with the data passed into the event as the "obj"
const ActivityDetail = ({ results, clickPrint }) => {
const renderedList = results.map((obj, index) => {
return (
<li key={index}>
<div className="service-container">
<Link to="/pdf" target="_blank" className="print-button-container">
<button
className="print-button"
onClick={() => clickPrint(obj)}
>Print</button>
</Link>
</div>
</li>
);
});
return (
<div>
<ul>
{renderedList}
</ul>
</div>
);
};
export default ActivityDetail;
This is the PDF document that should get the data when the Print button is clicked but props is undefined.
const styles = StyleSheet.create({
page: {
flexDirection: 'row',
},
section: {
margin: 10,
padding: 10,
flexGrow: 1
}
})
const PDFDocument = (props) => {
const { NameOfService } = props
console.log('props:', props)
return(
<PDFViewer className="pdf-viewer">
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.section}>
<Text>
{NameOfService}
</Text>
</View>
</Page>
</Document>
</PDFViewer>
)
}
export default PDFDocument
EDIT
So what I know have is a callback to a method that handles the newly set state.
onHandlePrint = (pdf) => {
this.setState({pdf}, () => {
this.updatePDF(this.state.pdf)
})
}
My new question is how do I send that data from the updatePDF method to the component ?
You should not setState within a setState callback function. Instead it should return the new state. This:
onHandlePrint = (pdf) => {
this.setState({pdf}, () => {
this.setState({pdfStatus: true})
});
};
should be:
onHandlePrint = (pdf) => {
this.setState(() => {pdf, pdfStatus: true});
};
But really if you don't need to use previous state you don't need to use a callback. Just do:
onHandlePrint = (pdf) => {
this.setState({pdf, pdfStatus: true});
};
Use async & await
onHandlePrint = async (pdf) => {
await this.setState({pdf}, () => {
this.setState({pdfStatus: true})
});
};

ReactJS: Material UI floating button onTouchTap calls url the ListItem beneath it

My Component looks like
const style = {
margin: 0,
top: 'auto',
right: 20,
bottom: 20,
left: 'auto',
position: 'fixed',
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false
}
}
toggleDrawer = () => this.setState({open: !this.state.open});
handleAddMenu = () => {
console.log("Opening New Menu Form");
this.props.history.push("/addMenu");
}
render() {
return (
<div>
<AppBar
title="SpicyVeggie"
iconClassNameRight="muidocs-icon-navigation-expand-more"
onLeftIconButtonTouchTap={this.toggleDrawer}
/>
<Drawer
docked={false}
width={300}
onRequestChange={this.toggleDrawer}
open={this.state.open}
>
<AppBar title="SpicyVeggie" onLeftIconButtonTouchTap={this.toggleDrawer} />
<MenuItem
primaryText="Menu"
containerElement={<Link to="/menu"/>}
onTouchTap={() => {
this.toggleDrawer()
}}
/>
<MenuItem
primaryText="Summary"
containerElement={<Link to="/summary"/>}
onTouchTap={() => {
this.toggleDrawer()
}}
/>
</Drawer>
<div className="container">
{this.props.children}
</div>
<div>
<FloatingActionButton
style={style}
mini={true}
secondary={true}
onTouchTap={this.handleAddMenu}>
<ContentAdd />
</FloatingActionButton>
</div>
</div>
);
}
}
export default App;
When I render the app it looks like
so when I click on Add, it triggers onTouchTap of the ListItem beneath the button.
The second time, when there is no ListItem under it, and I hit "Add", it calls the correct onTouchTap event for the Add.
How do I fix it for the first case, to give priority to onTouchTap for FloatingActionButton?
You'll want to intercept the event object of the action and stop the propagation of the click in the function that you call.
All you have to do is pass the event to the function and then call stopPropagation, like so:
handleAddMenu = (e) => {
e.stopPropagation();
console.log("Opening New Menu Form");
this.props.history.push("/addMenu");
}

React + MaterialUi handling actions in IconMenu and ListItem

I'm learning react and I try to create simple TODO based on material-ui, I have problem with handling IconMenu menu actions, menu is displayed in listItem element. At this moment I have no idea how trigger deleteItem function with item name as a parameter when delete action is clicked in menu.
const iconButtonElement = (
<IconButton touch={true} tooltip="More" tooltipPosition="bottom-left">
<MoreVertIcon color="black"/>
</IconButton>
);
const rightIconMenu = (
<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem value="done" leftIcon={<Done />}>Mark as done</MenuItem>
<MenuItem value="delete" leftIcon={<Delete />}>Delete</MenuItem>
</IconMenu>
);
class TodoElements extends Component {
deleteItem(nameProp)
{
this.props.delete(nameProp);
}
render() {
var listItemRender = function(item) {
return <ListItem key={item.name} primaryText={item.name} style={listItemStyle} rightIconButton={rightIconMenu}/>
};
listItemRender = listItemRender.bind(this);
return (
<List>
{this.props.items.map(listItemRender)}
</List>
)
}
}
As far as I can see, you should be able to add an onChange handler to your IconMenu. So your rightIconMenu can look like this:
const RightIconMenu = ({onChange}) => (
<IconMenu iconButtonElement={iconButtonElement} onChange={onChange}>
<MenuItem value="done" leftIcon={<Done />}>Mark as done</MenuItem>
<MenuItem value="delete" leftIcon={<Delete />}>Delete</MenuItem>
</IconMenu>
);
Then you can use it in your TodoElements like this:
class TodoElements extends Component {
constructor(props){
super(props);
this.state = {
items: props.items
};
}
createChangeHandler = (nameProp) => {
return (event, value) => {
if(value==="delete"){
this.deleteItem(nameProp);
}
};
}
deleteItem = (nameProp) =>
{
this.setState({
items: this.state.items.filter((item) => {
return item.name !== nameProp);
})
});
}
render() {
return (
<List>
{this.state.items.map((item) => {
<ListItem key={item.name} primaryText={item.name} style={listItemStyle}
rightIconButton={<RightIconMenu onChange={this.createChangeHandler(item.name)} />}/>
})}
</List>
)
}
}
Alternative
As an alternative solution you could bind an onClick handler to your delete MenuItem instead. I would probably implement it like this:
const RightIconMenu = ({onDelete}) => (
<IconMenu iconButtonElement={iconButtonElement}>
<MenuItem value="done" leftIcon={<Done />}>Mark as done</MenuItem>
<MenuItem value="delete" leftIcon={<Delete />} onClick={onDelete}>Delete</MenuItem>
</IconMenu>
);
And then replace the appropriate functions in the TodoElements:
createChangeHandler = (nameProp) => {
return (event, value) => {
this.deleteItem(nameProp);
};
}
render() {
return (
<List>
{this.state.items.map((item) => {
<ListItem key={item.name} primaryText={item.name} style={listItemStyle}
rightIconButton={<RightIconMenu onDelete={this.createDeleteHandler(item.name)} />}/>
})}
</List>
)
}
As for handling the state of your list of items, you should probably take a look at global state management such as Redux.
I think that a nicer approach would be using the onTouchTap every MenuItem has, So the onChange function won't have a switch or many if statements.
I'm actually using it when I iterate over all menu items,
To me it looks like this:
_.map(menuItems, (currItem, index) => {
return (<MenuItem primaryText={currItem.primaryText}
rightIcon={currItem.rightIcon}
leftIcon={currItem.leftIcon}
key={`menu-item-${index}`}
value={currItem.value}}
onTouchTap={currItem.onTouchTap}/>)
})

Categories

Resources