I need to update Parent table when data is changed from child edit form. Not getting the updated data in parent component.
I'm passing the data using all the passible ways as per my understanding. I'm not much experienced in react. So it would be really very helpful if someone can help me to understand where i'm making mistake.
PARENT Component is class component-
import Radium from 'radium';
import Dock from 'react-dock';
import {Divider} from '#material-ui/core';
import "react-tabulator/lib/styles.css";
import "react-tabulator/css/bootstrap/tabulator_bootstrap.min.css";
import { React15Tabulator, reactFormatter, MultiValueFormatter } from "react-tabulator";
import {Badge, Row, Button, Col,Card} from 'react-bootstrap';
import EditServer from '../EditServer';
import Confirm from '../../../ui-components/Confirm';
// import './index.scss';
import jsondata from '../../../constants/data/json/ApplicationServer.json';
const styles = {
root: {
fontSize: '16px',
color: '#999',
height: '100vh',
},
main: {
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
paddingTop: '30vh',
},
dockContent: {
// width: '400px',
height: '100%',
display: 'flex',
alignItems: 'left',
padding:'1rem',
overflowX:"hidden",
// justifyContent: 'center',
flexDirection: 'column',
},
remove: {
position: 'absolute',
zIndex: 1,
right: '10px',
top: '10px',
cursor: 'pointer'
}
}
const positions = ['left', 'top', 'right', 'bottom'];
const dimModes = ['transparent', 'none', 'opaque'];
// const dimStyle = ['all']
function SimpleBage (props: any) {
const rowData = props.cell._cell.row.data;
const cellValue = props.cell._cell.value || 'STANDALONE | DOWN | PRIMARY | BACKUP';
if(cellValue === 'STANDALONE'){
return <h5><Badge pill variant="warning" className="btn-size"
onClick={() => alert(rowData.serverStatus)}>
{cellValue}</Badge></h5>;
}
else if(cellValue === 'DOWN'){
return <h5><Badge pill variant="dark" className="btn-size">
{cellValue}</Badge></h5>;
}
else if(cellValue === 'PRIMARY'){
return <h5><Badge pill variant="success" className="btn-size">
{cellValue}</Badge></h5>;
}
else if(cellValue === 'BACKUP'){
return <h5><Badge pill variant="primary" className="btn-size">
{cellValue}</Badge></h5>;
}
else if(cellValue === 'STANDBY'){
return <h5><Badge pill variant="secondary" className="btn-size">
{cellValue}</Badge></h5>;
}
}
const editableColumns = [
{
title: "#",
field: "index",
width: 80,
dir:"asc",
// headerFilter: "input"
},
{
title: "Server Name",
field: "serverName",
hozAlign: "left",
dir:"asc",
},
{
title: "Server Status",
field: "serverStatus",
// width: 100,
hozAlign: 'left',
//formatter: MultiValueFormatter,
formatter: reactFormatter(<SimpleBage />),
},
{
title: "Server Group",
field: "serverGroup",
hozAlign: "left",
},
]
let data = [...jsondata];
const serverListArray =localStorage.getItem("configuration");
class ApplicationServer extends Component {
constructor(props) {
super(props);
this.state = {
data: data,
rowSelection:"",
stopBtnVisible:false,
startBtnVisble:true,
selectedServerName: "",
selectedServerGroup:"",
selectedServerStatus:"",
selectedServerStatusChange:this.state,
positionIdx: 2,
dimModeIdx: "transparent",
isVisible: false,
fluid: true,
customAnimation: true,
slow: true,
size: 0.30,
isServerStarted:this.state,
serverName:null,
rowIndex:null,
};
this.handleVisibleChange = this.handleVisibleChange.bind(this);
this.handleChange = this.handleChange.bind(this);
this.onClose= this.onClose.bind(this);
}
ref = null;
onClose = isVisible =>{
this.setState({ isVisible: false });
}
clearData = () => {
this.setState({ data: [] });
};
handleChange = (handler) =>{
// console.log("update handler data", serverGroup)
// const updatedServerGroup = event.target.value;
// console.log("handle form submit in table", this.state.selectedServerGroup, this.props.serverGroup);
console.log("handle form submit in table", handler);
this.setState({
...handler
//selectedServerGroup:handler.target.serverGroup
})
//console.log("handle form submit in table", this.state);
console.log("handle form submit in table", handler);
}
render(){
const options = {
height: 215,
movableRows: true,
history: true,
movableColumns: true,
resizableRows: true ,
reactiveData:true,
};
const duration = this.state.slow ? 1000 : 100;
const dur = duration / 1000;
const transitions = ['left', 'top', 'width', 'height']
.map(p => `${p} ${dur}s cubic-bezier(0, 1.5, 0.5, 1)`)
.join(',');
console.log("Render data:", serverListArray);
if(Array.isArray(serverListArray) && serverListArray.length>0){
data=[...serverListArray];
//jsondata= serverListArray;
}
return(
<div style={[styles.root]}>
<div style={{
position: 'relative',
height: 'calc(100vh - 50px)',
}}>
<Row className="table-control-bar">
<Col sm={8}><h5>Server Controls</h5></Col>
<Col sm={4} className="text-right">
<Button variant={this.state.failoverButtonVariant}
className="mr-2"
id="serverFailover"
onClick={this.handleServerFailover}
selectedserverName={this.selectedserverName}
disabled={this.state.failoverButtonDisabled}>Failover</Button>
<Button variant={this.state.editButtonVariant}
className="mr-2"
id="serverEdit"
selectedServerName={this.selectedServerName}
disabled={this.state.editButtonDisabled}
onClick={this.handleVisibleChange}>Edit</Button>
{this.state.stopBtnVisible === false &&
(
<Button
onClick={this.handleServerStart}
className="mr-2"
id="serverStart"
selectedserverName={this.selectedserverName}
visible={this.state.startBtnVisible}
variant={this.state.startButtonVariant}
disabled={this.state.startButtonDisabled}>Start
</Button>
)}
{this.state.startBtnVisible === false &&
(<Button onClick={this.handleServerStop}
className="mr-2"
id="serverStop"
selectedserverName={this.selectedserverName}
variant="primary">Stop</Button>)}
<Button variant={this.state.restartButtonVariant}
id="serverRestart"
onClick={this.handleServerRestart}
selectedserverName={this.selectedserverName}
disabled={this.state.restartButtonDisabled}>Restart</Button>
</Col>
</Row>
<div>
<React15Tabulator
columns={editableColumns}
data={data}
selectable={1}
rowClick={this.rowClick}
options={options}
data-custom-attr="test-custom-attribute"
className={"custom-css-class" + this.state.rowSelection}
tooltips={true}
layout={"fitData"}
/>
</div>
</div>
<div>
<Dock position={positions[this.state.positionIdx]}
size={this.state.size}
dimMode={dimModes[this.state.dimModeIdx]}
isVisible={this.state.isVisible}
onVisibleChange={this.handleVisibleChange}
fluid={this.state.fluid}
dimStyle={{ background: 'rgba(0, 0, 100, 0.2)'}, {pointerEvents:'all !important'}}
dockStyle={this.state.customAnimation ? { transition: transitions } : null, {top:'6%'} }
// ,{top:'13%'}
dockHiddenStyle={this.state.customAnimation ? {
transition: [transitions, `opacity 0.01s linear ${dur}s`].join(',')
} : null}
duration={duration}>
{({ position, isResizing }) =>
<div style={[styles.dockContent]} className="modal-container">
<h4>Edit Server Group</h4>
<Divider />
<EditServer
handleInputChange={this.handleChange}
// handleChange = {e => console.log(e)}
serverName={this.state.selectedServerName}
serverGroup= {this.state.selectedServerGroup}
onClose={this.onClose} />
<span onClick={() => this.setState({ isVisible: false })}
style={styles.remove} title="close">✖</span>
</div>
}
</Dock>
</div>
</div>
)
}
handleSizeChange = size => {
this.setState({ size });
}
}
export default ApplicationServer = Radium(ApplicationServer);
CHILD Component is functional component-
import {func, string} from 'prop-types';
import {makeStyles } from '#material-ui/core';
import {Row, Col, Button, Form} from 'react-bootstrap';
import toast from '../../../ui-components/Toaster';
import Input from '../../../ui-components/Input';
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(2),
},
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300,
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
margin: 2,
},
noLabel: {
marginTop: theme.spacing(3),
},
}));
function getStyles(name, personName, theme) {
return {
fontWeight:
personName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}
export default function EditServer(props) {
const {onClose} = props;
let serverGroup = props.serverGroup;
const initialFormState = {
id: null,
serverName: props.serverName,
serverGroup: props.serverGroup,
saveDisabled: true,
cancelDisabled:true,
showDockModal:false,
edited:false,
}
// console.log("inside child", initialFormState);
const [handler, setHandler] = useState();
//const ref = useRef(null);
const handleInputChange = (event) => {
//event.preventDefault();
console.log("edit server props input change", handler);
let { name, value } = event.target;
//handler.serverGroup = event.target.value;
setHandler({...handler, [name]: value, edited:true});
console.log("inside edit form change:", handler, value, name);
}
const handleFormSubmit = (event) => {
//console.log('inside handleFormSubmit', event, props);
event.preventDefault();
onClose();
if (handler.edited){
//let newServerGroup= handler.serverGroup;
//console.log("form submit", handler);
setHandler({
...handler,
edited:false,
});
console.log("inside edit form change:", handler, event)
// props.serverGroup=newServerGroup;
// return handleInputChange(handler.serverGroup.value);
return toast.success(initialFormState.serverGroup + " Server Group Changed to SIMULATION !");
}
}
return (
<React.Fragment>
<Form className="scrollY"
onSubmit={handleFormSubmit}
>
<Row>
<Col sm={4} className="text-align-right">
<label htmlFor="serverName">Server Name<span className="red">*</span></label>
</Col>
<Col sm={8}>
<Input
type="text"
required
id="severName"
name="severName"
// label="Handler Name"
fullWidth
autoComplete="given-name"
value={initialFormState.serverName}
onChange={handleInputChange}
disabled="true"
/>
</Col>
</Row>
<Row>
<Col sm={4} className="text-align-right">
<label htmlFor="serverGroup" required>Server Group<span className="red">*</span></label>
</Col>
<Col sm={8}>
<Input
type="string"
required
id="serverGroup"
name="serverGroup"
// label="Handler Speed"
fullWidth
autoComplete="family-name"
defaultValue={initialFormState.serverGroup}
onChange={handleInputChange}
/>
</Col>
</Row>
<Row>
<Col sm={4}>
{/* <Button variant="secondary" disabled className="btn-custom" onClick={handleClose}>Cancel</Button>*/}
</Col>
<Col sm={8}>
<Button type="submit"
variant="primary"
className="btn-custom"
>
Submit
</Button>
</Col>
</Row>
</Form>
</React.Fragment>
);
}
EditServer.propTypes = {
onSuccess: func,
};
I'm using react tabulator.
Appreciate the help. Thank you.
Related
I am using URL.createObjectURL to create image and pass it to an other component. In my NotificationStepper URL.createObjectURL is working just fine but in another Createwebsite component is i am getting this error TypeError: Failed to execute 'createObjectURL' on 'URL': No function was found that matched the signature provided..
following is my NotificationStepper component.
const styles = {
transparentBar: {
backgroundColor: 'transparent !important',
boxShadow: 'none',
paddingTop: '25px',
color: '#FFFFFF'
},
dialogPaper: {
minHeight: '400px',
maxHeight: '400px',
},
};
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const useStyles = makeStyles((theme) =>
createStyles({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}),
);
function getSteps() {
return ['Create', 'Audience', 'Timing'];
}
function getStepContent(step, $this) {
return (
<div className="row">
<CardBox styleName="col-lg-12"
heading="">
<form className="row" noValidate autoComplete="off" style={{ "flex-wrap": "no-wrap", "flex-direction": "column" }}>
<div className="col-md-12 col-12" style={{ "margin-bottom": "15px", "display": "flex", "align-items": "center" }}>
<Tooltip title="Use featured image of the landing page here. Works on Chrome, Opera, Edge. Recommended aspect ratio 2:1. Jpg, Jpeg, PNG file types only.">
<IconButton aria-label="">
<HelpOutlineIcon />
</IconButton>
</Tooltip>
<span style={{ "width": "20%", "margin-right": "20px" }}>Banner: </span>
<div className="inputfile">
Upload
<input className='hide_file' type="file" onChange={$this.onBannerChange} />
</div>
{$this.state.banner ?
<span>{$this.state.icon.banner}</span>
: null
}
</div>
</form>
</div >
);
}
class NotificationStepper extends React.Component {
state = {
banner: '',
crop: { x: 0, y: 0 },
zoom: 1,
aspect: 1,
croppedAreaPixels: null,
croppedImage: null,
showDialog: false,
};
onBannerChange = event => {
this.setState({ banner: event.target.files[0] });
this.setState({showDialog: true})
};
onIconChange = event => {
this.setState({ icon: event.target.files[0] });
this.setState({showDialogIcon: true})
};
onCropChange = crop => {
this.setState({ crop })
};
onCropComplete = (croppedArea, croppedAreaPixels) => {
this.setState({croppedAreaPixels: croppedAreaPixels});
};
CropImageBanner = async ()=> {
const croppedImage = await getCroppedImg(
URL.createObjectURL(this.state.banner),
this.state.croppedAreaPixels,
0
);
this.setState({ banner: croppedImage });
this.closeDialog();
};
closeDialog = () => {
this.setState({showDialog: false});
}
onZoomChange = zoom => {
this.setState({ zoom })
};
render() {
const steps = getSteps();
const { activeStep } = this.state;
if (this.props.showSuccessMessage) {
console.log('dispatch');
setTimeout(() => {
this.props.hideMessage();
}, 100);
}
if (this.props.showMessage) {
console.log('dispatch');
setTimeout(() => {
this.props.hideCompaignAlert();
}, 100);
}
const { classes } = this.props;
return (
<div>
{this.state.banner ?
// <span>{$this.state.banner.name}</span>
<Dialog
classes={{paper: classes.dialogPaper }}
fullWidth={true}
maxWidth='sm'
open={this.state.showDialog}
onClose={this.closeDialog}
>
<DialogTitle>
</DialogTitle>
<DialogContent>
<div className="App-crop" >
<div className="crop-container-div">
<Cropper
image={URL.createObjectURL(this.state.banner)}
crop={this.state.crop}
zoom={this.state.zoom}
aspect={this.state.aspect}
onCropChange={this.onCropChange}
onCropComplete={this.onCropComplete}
onZoomChange={this.onZoomChange}
/>
</div>
<div className="controls">
<Slider
value={this.state.zoom}
min={1}
max={3}
step={0.1}
aria-labelledby="Zoom"
onChange={(e, zoom) => this.onZoomChange(zoom)}
/>
</div>
</div>
</DialogContent>
<DialogActions>
<Button onClick={() => this.closeDialog()} color="secondary">
Cancel
</Button>
<Button onClick={() =>
this.CropImageBanner()
} color="primary">
Crop
</Button>
</DialogActions>
</Dialog>
:
null
}
<Stepper className="MuiPaper-root-custom" activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => {
return (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent className="pb-3">
<Typography>{getStepContent(index, this)}</Typography>
<div className="mt-2">
<div>
<Button
disabled={activeStep === 0}
onClick={this.handleBack}
className="jr-btn"
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={this.handleNext}
className="jr-btn"
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
</StepContent>
</Step>
);
})}
</Stepper>
{activeStep === steps.length && (
// <Paper square elevation={0} className="p-2">
/*<Typography>All steps completed - you"re finished</Typography>*/
<div>
<Button onClick={this.sendNotification} disabled={!this.isFormValid} color="primary" style={{ "align-self": "flex-end", "border": "1px solid", "margin-left": "10px", "margin-bottom": "40px" }} size="small" className="col-md-2 col-2">Create</Button>
<Button onClick={this.handleReset} color="primary" style={{ "align-self": "flex-end", "border": "1px solid", "margin-left": "10px", "margin-bottom": "40px" }} size="small" className="col-md-2 col-2" color="primary">Reset</Button>
)}
{this.props.showMessage && NotificationManager.error(this.props.alertMessage) ?
<NotificationContainer /> : null
}
{this.props.showSuccessMessage && NotificationManager.success(this.props.successMessage)}
<NotificationContainer />
</div>
);
}
}
const mapDispatchToProps = dispatch => ({
showCompaignAlert: (message) => dispatch(showCompaignAlert(message)),
CreateCampaign: (data) => dispatch(CreateCampaign(data)),
hideMessage: () => dispatch(hideMessage()),
hideCompaignAlert: () => dispatch(hideCompaignAlert()),
showCampaignLoader: () => dispatch(showCampaignLoader())
});
const mapStateToProps = state => ({
loader: state.Campaigns.loader,
allWebsites: state.Websites.allWebsites,
alertMessage: state.Campaigns.alertMessage,
showMessage: state.Campaigns.showMessage,
successMessage: state.Campaigns.successMessage,
showSuccessMessage: state.Campaigns.showSuccessMessage,
});
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles) (NotificationStepper)));
and following is my CreateWebsite
import React from "react";
import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import Slide from "#material-ui/core/Slide";
import DialogTitle from "#material-ui/core/DialogTitle";
import DialogContent from "#material-ui/core/DialogContent";
import DialogActions from "#material-ui/core/DialogActions";
import FormControl from "#material-ui/core/FormControl";
import TextField from "#material-ui/core/TextField";
import {connect} from "react-redux";
import {createWebsite, hideSuccessMessage, hideAlertCreateWebsite} from "../../../actions/Websites";
import {NotificationContainer, NotificationManager} from "react-notifications";
import InputAdornment from "#material-ui/core/InputAdornment";
import IconButton from "#material-ui/core/IconButton";
import getCroppedImg from "../Send Notification/cropImage";
import Cropper from "react-easy-crop";
import Slider from "#material-ui/core/Slider";
import {withStyles} from "#material-ui/core/styles";
const styles = {
transparentBar: {
backgroundColor: 'transparent !important',
boxShadow: 'none',
paddingTop: '25px',
color: '#FFFFFF'
},
dialogPaper: {
minHeight: '400px',
maxHeight: '400px',
},
};
class CreateWebsiteComponent extends React.Component {
state = {
title: '',
url: '',
logo: '',
showDialog: false,
crop: { x: 0, y: 0 },
zoom: 1,
aspect: 1,
croppedAreaPixels: null,
croppedImage: null,
};
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
onFileChange = event => {
this.setState({ logo: event.target.files[0] });
this.setState({ showDialog: true})
};
createWebsite = (title, url, logo) => {
let temp = 'https://' + url;
this.props.createWebsite({title, url , logo})
};
onCropChange = crop => {
this.setState({ crop })
};
onCropComplete = (croppedArea, croppedAreaPixels) => {
this.setState({croppedAreaPixels: croppedAreaPixels});
};
closeDialog = () => {
this.setState({showDialog: false});
};
onZoomChange = zoom => {
this.setState({ zoom })
};
CropImageBanner = async ()=> {
const croppedImage = await getCroppedImg(
URL.createObjectURL(this.state.logo),
this.state.croppedAreaPixels,
0
);
this.setState({ logo: croppedImage });
this.closeDialog();
};
render() {
if (this.props.showSuccessMessage) {
setTimeout(() => {
this.props.hideSuccessMessage();
}, 100);
}
if (this.props.showAlertCreateWebsite_) {
setTimeout(() => {
this.props.hideAlertCreateWebsite();
}, 100);
}
const { title, url, logo} = this.state;
const openDialog = this.props.openDialog;
const { classes } = this.props;
return (
<div>
<Dialog
classes={{paper: classes.dialogPaper }}
fullWidth={true}
maxWidth='md'
open={this.state.showDialog}
onClose={this.closeDialog}
>
<DialogTitle>
</DialogTitle>
<DialogContent>
<div className="App-crop" >
<div className="crop-container-div">
<Cropper
image={this.state.log}
crop={this.state.crop}
zoom={this.state.zoom}
aspect={this.state.aspect}
onCropChange={this.onCropChange}
onCropComplete={this.onCropComplete}
onZoomChange={this.onZoomChange}
/>
</div>
<div className="controls">
<Slider
value={this.state.zoom}
min={1}
max={3}
step={0.1}
aria-labelledby="Zoom"
onChange={(e, zoom) => this.onZoomChange(zoom)}
/>
</div>
</div>
</DialogContent>
<DialogActions>
<Button onClick={() => this.closeDialog()} color="secondary">
Cancel
</Button>
<Button onClick={() =>
this.CropImageBanner()
} color="primary">
Crop
</Button>
</DialogActions>
</Dialog>
<Dialog
open={openDialog}
onClose={this.props.closeDialog}
>
<DialogTitle>
{"Create Website"}
</DialogTitle>
<DialogContent>
<FormControl className="w-100">
<TextField
style={{'margin-right': "10px"}}
className="col-md-11 col-11"
id="name"
label="Title"
value={this.state.title}
onChange={this.handleChange('title')}
margin="normal"
fullWidth
/>
<TextField
style={{'margin-right': "10px"}}
className="col-md-11 col-11"
id="name"
label="URL"
value={this.state.url}
onChange={this.handleChange('url')}
margin="normal"
fullWidth
InputProps={{
startAdornment: (
<InputAdornment className='positionEnd' position="start">
https://
</InputAdornment>
),
}}
/>
<div style={{"margin-top": "20px"}} className='col-md-11 col-sm-12 col-12 flex-class-custom'>
<span style={{"margin-right": "20px"}}>Icon: </span>
<div className="inputfile">
Upload
<input className='hide_file' style={{"width": "80%"}} type="file" onChange={this.onFileChange} />
</div>
{this.state.logo ?
<span>{this.state.logo.name}</span>
: null
}
</div>
</FormControl>
</DialogContent>
<DialogActions>
<Button onClick={this.props.closeDialog} color="secondary">
Cancel
</Button>
<Button onClick={() => this.createWebsite(title, url , logo)
} color="primary">
Create
</Button>
</DialogActions>
</Dialog>
{this.props.showAlertCreateWebsite_ && NotificationManager.error(this.props.alertMessage)}
<NotificationContainer/>
{this.props.showSuccessMessage && NotificationManager.success(this.props.successMessage)}
<NotificationContainer/>
</div>
)
}
}
const mapDispatchToProps = dispatch => ({
createWebsite: (payload) => dispatch(createWebsite(payload)),
hideSuccessMessage: () => dispatch(hideSuccessMessage()),
hideAlertCreateWebsite: () => dispatch(hideAlertCreateWebsite())
});
const mapStateToProps = state => ({
loader : state.Websites.loader ,
alertMessage : state.Websites.alertMessage ,
showAlertCreateWebsite_ : state.Websites.showAlertCreateWebsite_,
showSuccessMessage : state.Websites.showSuccessMessage,
successMessage : state.Websites.successMessage
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(CreateWebsiteComponent));
// export default CreateWebsiteComponent;
I'm building an application, where there is a form presented with different steps. In all the steps but one, I manage to provide the necessary functions as props to make some operations such as 'handleNext', 'handleBack' or 'handleChange'.
Nevertheless, in the last step, represented in the class SuccessForm, when I try to execute the 'handleDownload' function, I get the following error:
TypeError: this.props.handleDownload is not a function
Here it is the SuccessForm.js class:
export class SuccessForm extends Component {
constructor() {
super();
}
download = e => {
e.preventDefault();
this.props.handleDownload();
}
render() {
return (
<React.Fragment>
<Grid container>
<Grid item xs={12} sm={2}>
<DirectionsWalkIcon fontSize="large" style={{
fill: "orange", width: 65,
height: 65
}} />
</Grid>
<Grid>
<Grid item xs={12} sm={6}>
<Typography variant="h5" gutterBottom>
Route created
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="subtitle1">
Your new track was succesfully created and saved
</Typography>
</Grid>
</Grid>
<Tooltip title="Download" arrow>
<IconButton
variant="contained"
color="primary"
style={{
marginLeft: 'auto',
// marginRight: '2vh'
}}
onClick={this.download}
>
<GetAppIcon fontSize="large" style={{ fill: "orange" }} />
</IconButton>
</Tooltip>
</Grid>
</React.Fragment>
)
}
}
The entire NewRouteForm.js:
import React, { Component } from 'react'
import { makeStyles, MuiThemeProvider } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Stepper from '#material-ui/core/Stepper';
import Step from '#material-ui/core/Step';
import StepLabel from '#material-ui/core/StepLabel';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import DataForm from '../stepper/dataform/DataForm';
import ReviewForm from '../stepper/reviewform/ReviewForm';
import MapForm from '../stepper/mapform/MapForm';
import NavBar from '../../graphic interface/NavBar';
import DirectionsWalkIcon from '#material-ui/icons/DirectionsWalk';
import Avatar from '#material-ui/core/Avatar';
import CheckCircleOutlineOutlinedIcon from '#material- ui/icons/CheckCircleOutlineOutlined';
import FilterHdrIcon from '#material-ui/icons/FilterHdr';
import Grid from '#material-ui/core/Grid';
import SuccessForm from '../stepper/success/SuccessForm';
import { withStyles } from '#material-ui/styles';
export class NewRouteForm extends Component {
state = {
activeStep: 0,
name: '',
description: '',
date: new Date(),
photos: [],
videos: [],
points: []
};
handleNext = () => {
const { activeStep } = this.state;
this.setState({ activeStep: activeStep + 1 });
};
handleBack = () => {
const { activeStep } = this.state;
this.setState({ activeStep: activeStep - 1 });
};
handleChange = input => e => {
this.setState({ [input]: e.target.value });
}
handleDateChange = date => {
this.setState({ date: date });
}
handleMediaChange = (selectorFiles: FileList, code) => { // this is not an error, is TypeScript
switch (code) {
case 0: // photos
this.setState({ photos: selectorFiles });
break;
case 1: // videos
this.setState({ videos: selectorFiles });
break;
default:
alert('Invalid media code!!');
console.log(code)
break;
}
}
handleMapPoints = points => {
this.setState({ points: points })
}
// ###########################
// Download and Upload methods
// ###########################
handleDownload = () => {
// download route
console.log("DOWNLOAD")
alert("DOWNLOAD");
}
upload = () => {
// upload route
}
render() {
const { activeStep } = this.state;
const { name, description, date, photos, videos, points } = this.state;
const values = { activeStep, name, description, date, photos, videos, points };
const { classes } = this.props;
return (
<MuiThemeProvider>
<React.Fragment>
<NavBar />
<main className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<FilterHdrIcon fontSize="large" />
</Avatar>
<Typography component="h1" variant="h4" align="center">
Create your own route
</Typography>
<Stepper activeStep={activeStep} className={classes.stepper}>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
<React.Fragment>
{activeStep === steps.length ? (
<SuccessForm />
) : (
<React.Fragment>
{getStepContent(activeStep,
values,
this.handleNext,
this.handleBack,
this.handleChange,
this.handleDateChange,
this.handleMediaChange,
this.handleMapPoints,
this.handleDownload
)}
</React.Fragment>
)}
</React.Fragment>
</Paper>
</main>
</React.Fragment>
</MuiThemeProvider>
)
}
}
const steps = ['Basic data', 'Map', 'Review your route'];
function getStepContent(step,
values,
handleNext,
handleBack,
handleChange,
handleDateChange,
handleMediaChange,
handleMapPoints,
handleDownload) {
switch (step) {
case 0:
return <DataForm
handleNext={handleNext}
handleChange={handleChange}
handleDateChange={handleDateChange}
handleMediaChange={handleMediaChange}
values={values}
/>;
case 1:
return <MapForm
handleNext={handleNext}
handleBack={handleBack}
handleMapPoints={handleMapPoints}
values={values}
/>;
case 2:
return <ReviewForm
handleNext={handleNext}
handleBack={handleBack}
values={values}
/>;
case 3:
return <SuccessForm
handleDownload={handleDownload}
/>;
default:
throw new Error('Unknown step');
}
}
const useStyles = theme => ({
layout: {
width: 'auto',
marginLeft: theme.spacing(2),
marginRight: theme.spacing(2),
[theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {
width: 600,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing(3),
marginBottom: theme.spacing(3),
padding: theme.spacing(2),
[theme.breakpoints.up(600 + theme.spacing(3) * 2)]: {
marginTop: theme.spacing(6),
marginBottom: theme.spacing(6),
padding: theme.spacing(3),
},
},
stepper: {
padding: theme.spacing(3, 0, 5),
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
},
button: {
marginTop: theme.spacing(3),
marginLeft: theme.spacing(1),
},
avatar: {
marginLeft: 'auto',
marginRight: 'auto',
backgroundColor: theme.palette.warning.main,
},
icon: {
width: 65,
height: 65,
},
grid: {
marginLeft: theme.spacing(2),
}
});
export default withStyles(useStyles)(NewRouteForm);
Try calling super(props) in the constructor:
constructor(props) {
super(props);
}
and passing function with this instance (this.handleDownload) as it is a class property:
<SuccessForm handleDownload={this.handleDownload} />
Update:
You have a bug on the last step when you not passing a property:
activeStep === steps.length ? <SuccessForm handleDownload={this.handleDownload}/>
Assuming that you have a class in your parent Component, what you're missing is the this keyword in the function reference...
case 3:
return <SuccessForm
handleDownload={this.handleDownload}
/>;
My component data is fetched based on the route entered - /reports/:id i.e. /reports/1
the "1" following the /reports/ is retrieved by match.params.id which I then make a dispatch call to the following url:
fetchDashData(`http://ee-etap.devops.fds.com/api/etap/v1/templates/template/report/${match.params.id}`)
When the user enter an invalid id, i.e. /reports/a - I want to redirect the user back to /reports which displays a landing page and error message, as such:
return <Redirect to={{
pathname: '/reports',
state: { templateId: match.params.id } }}
/>;
This all works fine until when the user try to visit a valid 'id', i.e. /reports/1 right after the erroneous one - /reports/a, in which the user is immediately redirected back to the /reports page because the fetch call is asynchronous and haven't finished loading the data for /reports/1.
I already have isLoading state defined.. but how can I prevent this from happening?
ReportsDashboard.jsx ( /reports/:id)
class ChartsDashboard extends React.Component {
componentDidMount() {
const { fetchDashData, data, isLoading, hasErrored, match } = this.props;
if ( match.params && match.params.id ) {
fetchDashData(`http://ee-etap.devops.fds.com/api/etap/v1/templates/template/report/${match.params.id}`);
}
}
render() {
const { data, hasErrored, isLoading, classes, match } = this.props;
if ( isLoading ) {
return (
<div style={{ margin: '0 auto', textAlign: 'center' }}>
<CircularProgress size={50} color="secondary" />
</div>
);
}
if ( data && data !== null ) {
const { TemplateReport } = data;
const {
errorBarChart, historyTriggers, historyLineChart, jobs, lastBuildDonutChart, features,
} = TemplateReport;
if (errorBarChart.length === 0) {
// error in data
return <Redirect to={{
pathname: '/reports',
state: { templateId: match.params.id } }}
/>;
}
const keys = [];
errorBarChart.forEach((errorItem) => {
Object.keys(errorItem).forEach((errorKey) => {
if (errorKey !== 'category') {
keys.push(errorKey);
}
});
});
if (match.params.id) {
return (
<div className="page-container">
<Grid container spacing={24}>
<Grid item xs={12} lg={4}>
<Paper className={classes.paper}>
<h4 className={classes.heading}>Error By Categories</h4>
<div style={{ height: '350px' }}>
<ResponsiveBar
data={errorBarChart}
keys={keys}
indexBy="category"
margin={{
top: 50,
right: 50,
bottom: 50,
left: 50,
}}
padding={0.1}
colors="paired"
colorBy="id"
axisBottom={{
orient: 'bottom',
tickSize: 5,
tickPadding: 5,
tickRotation: 0,
legend: 'CATEGORY',
legendPosition: 'middle',
legendOffset: 36,
}}
axisLeft={{
orient: 'left',
tickSize: 5,
tickPadding: 5,
tickRotation: 0,
legend: 'ERROR COUNT',
legendPosition: 'middle',
legendOffset: -40,
}}
labelSkipWidth={12}
labelSkipHeight={12}
labelTextColor="inherit:darker(1.6)"
animate
motionStiffness={90}
motionDamping={15}
/>
</div>
</Paper>
</Grid>
<Grid item xs={12} lg={4}>
<Paper className={classes.paper}>
<h4 className={classes.heading}>Pass Rate %</h4>
<div style={{ height: '350px' }}>
<ResponsivePie
colors="paired"
colorBy={this.pieColors}
margin={{
top: 40,
right: 40,
bottom: 40,
left: 40,
}}
data={lastBuildDonutChart}
animate
defs={[
linearGradientDef('gradientRed', [{ offset: 0, color: 'red' }, { offset: 100, color: '#ffcdd2', opacity: 0.3 }]),
linearGradientDef('gradientYellow', [{ offset: 0, color: 'yellow' }, { offset: 100, color: '#f7bf18a3', opacity: 0.3 }]),
linearGradientDef('gradientGreen', [{ offset: 0, color: '#38da3e' }, { offset: 100, color: '#38da3e', opacity: 0.3 }]),
]}
fill={[
{ match: { id: 'Fail' }, id: 'gradientRed' },
{ match: { id: 'Pass' }, id: 'gradientGreen' },
{ match: { id: 'Undefined' }, id: 'gradientYellow' },
]}
radialLabelsSkipAngle={10}
radialLabelsTextXOffset={6}
radialLabelsTextColor="#333333"
radialLabelsLinkOffset={0}
radialLabelsLinkDiagonalLength={8}
radialLabelsLinkHorizontalLength={7}
radialLabelsLinkStrokeWidth={1}
radialLabelsLinkColor="inherit"
innerRadius={0.5}
padAngle={0.7}
cornerRadius={3}
/>
</div>
</Paper>
</Grid>
<Grid item xs={12} lg={4}>
<Paper className={classes.paper}>
<h4 className={classes.heading}>Jobs Triggered</h4>
<JobsTable data={jobs} templateId={match.params.id} />
</Paper>
</Grid>
<Grid item xs={12} lg={12}>
<Paper className={classes.paper}>
<h4 className={classes.heading}>Scenarios Table</h4>
<Tooltip title="Scenario Report">
<a href={`/reports/${match.params.id}/scenarioHistory`} rel="noopener noreferrer">
<IconButton aria-label="Scenario Report">
<AssignmentIcon />
</IconButton>
</a>
</Tooltip>
<ScenariosTable data={features} />
</Paper>
</Grid>
<Grid item xs={12} lg={12}>
<Paper className={classes.paper}>
<h4 className={classes.heading}>Execution History</h4>
<div style={{ height: '400px' }}>
<ResponsiveLine
colors="paired"
colorBy="id"
margin={{
top: 20,
right: 20,
bottom: 60,
left: 80,
}}
data={historyLineChart}
enableArea={true}
animate
yScale={{ type: 'linear', stacked: true }}
/>
</div>
</Paper>
</Grid>
<Grid item xs={12}>
<Paper className={classes.paper}>
<h4 className={classes.heading}>Previous Builds</h4>
<PreviousBuildsTable data={historyTriggers} templateId={match.params.id}/>
</Paper>
</Grid>
</Grid>
</div>
);
}
}
// error in data
return <Redirect to={{
pathname: '/reports',
state: { templateId: match.params.id } }}
/>;
}
}
const mapStateToProps = state => ({
data: state.reports.data,
hasErrored: state.reports.hasErrored,
isLoading: state.reports.isLoading,
});
const mapDispatchToProps = dispatch => ({
fetchDashData: url => dispatch(chartDataFetch(url)),
});
export default compose(
withStyles(styles),
withRouter,
connect(
mapStateToProps,
mapDispatchToProps,
),
)(ChartsDashboard);
BrowseReport.jsx (/reports/)
class BrowseReports extends React.Component {
constructor(props) {
super(props);
this.state = {
searchVal: '',
errorMsg: '',
}
this.onSearchChange = this.onSearchChange.bind(this);
this.goToTemplateReport = this.goToTemplateReport.bind(this);
}
componentDidMount() {
if (this.props.location && this.props.location.state && this.props.location.state.templateId) {
this.state.errorMsg = `Template Name "${this.props.location.state.templateId}" does not exist, please try again`;
this.props.history.replace('/reports', null);
}
}
onSearchChange(val) {
this.setState({ searchVal: val });
}
goToTemplateReport(val) {
this.props.history.push(`/reports/${val}`);
}
render() {
const { classes, location } = this.props;
const { searchVal, errorMsg } = this.state;
return (
<div className="page-container" style={{ textAlign: 'center' }}>
<Grid container justify="center" spacing={24}>
<Grid item xs={12} lg={8}>
{/* dashData Error */}
<h4 className={classes.errorMsg}>
{/* ERROR MESSAGE HERE */}
{errorMsg}
</h4>
<SearchBar
value={this.state.searchVal}
placeholder='Search for Template Name'
onChange={(value) => this.onSearchChange(value)}
onRequestSearch={(value) => this.goToTemplateReport(value)}
style={{
margin: '0 auto',
}}
/>
</Grid>
<Grid item xs={12} lg={6}>
<Paper className={classes.paper}>
<CompletedJobsTable></CompletedJobsTable>
</Paper>
</Grid>
<Grid item xs={12} lg={6}>
<Paper className={classes.paper}>
<ActiveJobsTable></ActiveJobsTable>
</Paper>
</Grid>
</Grid>
</div>
)
}
}
export default compose(
withStyles(styles),
withRouter
)(BrowseReports);
actions.jsx
export const chartDataHasErrored = hasErrored => ({
type: CHARTS_DATA_HAS_ERRORED,
payload: { hasErrored },
});
export const chartDataIsLoading = isLoading => ({
type: CHARTS_DATA_IS_LOADING,
payload: { isLoading },
});
export const chartDataFetchSuccess = data => ({
type: CHARTS_DATA_FETCH_SUCCESS,
payload: { data },
});
export const chartDataFetch = url => (dispatch) => {
dispatch(chartDataIsLoading(true));
fetch(url, { mode: 'cors' })
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
})
.then(response => response.json())
.then((items) => {
dispatch(chartDataFetchSuccess(items));
})
.catch((error) => {
dispatch(chartDataHasErrored(error));
});
};
reducers.jsx
import { CHARTS_DATA_FETCH_SUCCESS, CHARTS_DATA_IS_LOADING, CHARTS_DATA_HAS_ERRORED } from '../../../store/actions';
const INITIAL_STATE = {
hasErrored: null,
isLoading: true,
data: {},
}
const reportsDashboardReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case CHARTS_DATA_HAS_ERRORED:
return {
...state,
hasErrored: action.payload.hasErrored,
isLoading: false,
};
case CHARTS_DATA_IS_LOADING:
return {
...state,
isLoading: action.payload.isLoading,
hasErrored: null,
};
case CHARTS_DATA_FETCH_SUCCESS:
return {
...state,
isLoading: false,
data: action.payload.data,
};
default:
return state;
}
};
export default reportsDashboardReducer;
You need to save templateId in the global state (set it when data loaded). In component data need to be shown only if templateId from the path equal to the templateId from the global state.
I have simple to-do app and trying to do validation for adding new task mainly I want to prevent user to add a blank task to the list - my React skills are very poor so please forgive this silly question.
if you have any idea how to solve my problem please let me know thanks!
import React, { Component } from 'react';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import Typography from 'material-ui/Typography';
import Paper from 'material-ui/Paper';
import Grid from 'material-ui/Grid';
import TextField from 'material-ui/TextField';
import List from './List';
import '../App.css';
class App extends Component {
state = {
query: '',
inputValue: '',
todos: [
{ value: 'Naumiej się Reacta', done: false },
{ value: 'Pucuj trzewiki ', done: true },
],
direction: 'row',
justify: 'left',
alignItems: 'left',
}
handleChange = (evt) => {
this.setState({ inputValue: evt.target.value });
}
removeMe = (index) => {
this.setState({
todos: this.state.todos.filter((_, i) => i !== index)
})
}
searchChanged = (evt) => {
this.setState({ query: evt.target.value })
}
handleSubmit = (evt) => {
if (evt.keyCode === 13) {
const newTodo = {
value: this.state.inputValue,
done: false
};
const todos = this.state.todos.concat(newTodo);
this.setState({ todos: todos, inputValue: '' })
}
}
render() {
return (
<Grid item xs={12} style={{ padding: 30, display: 'flex' }}>
<div className="App">
<Typography type="body1'" color="inherit" text-align='left'>
<AppBar position="static" color="default" style={{ flexDirection: 'center' }}>
<Toolbar>
<TextField
style={{ float: 'left', paddingRight: 40, }}
placeholder="Add Task ..."
onChange={this.handleChange}
inputValue={this.state.inputValue}
onKeyDown={this.handleSubmit}
>
</TextField>
<TextField ype="text" placeholder="Search..." onChange={this.searchChanged} />
</Toolbar>
</AppBar>
</Typography>
<Paper>
<List style={{ marginTop: 90 }}
removeMe={this.removeMe}
todos={this.state.todos}
query={this.state.query}
/>
</Paper>
</div>
</Grid>
);
}
}
export default App;
I think you did correctly. Just return before submit or check for value with enter key.
handleSubmit = (evt) => {
if (evt.keyCode === 13 && this.state.inputValue) {
const newTodo = {
value: this.state.inputValue,
done: false
};
const todos = this.state.todos.concat(newTodo);
this.setState({todos: todos, inputValue: ''})
}
}
I'm trying to run a project, but the same is displaying an error in the console related to a function I have, the error is as follows:
"63:25 error Parsing error: Unexpected token, expected (function toggleDrawer = (open) => () => {^ state = {"
My project is this:
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { withStyles } from 'material-ui/styles';
import AppBar from 'material-ui/AppBar';
import Drawer from 'material-ui/Drawer';
import Button from 'material-ui/Button';
import IconButton from 'material-ui/IconButton';
import Toolbar from 'material-ui/Toolbar';
import MenuIcon from 'material-ui-icons/Menu';
import TextField from 'material-ui/TextField';
import Paper from 'material-ui/Paper';
import Grid from 'material-ui/Grid';
import '../assets/scss/main.scss';
import img from '../assets/images/react.png';
const styles = theme => ({
root: {
width: '100%',
},
flex: {
flex: 1,
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
inputProps: {
step: 300,
},
button: {
margin: theme.spacing.unit,
},
input: {
display: 'none',
},
paper: {
padding: 50,
textAlign: 'center',
border: '5px solid black',
width: '100%',
},
paper1: {
backgroundColor: 'red',
marginTop: '13%',
},
img: {
width: '45%',
},
appbar: {
marginLeft: '-20.20%',
marginTop: '-20%',
width: '139.99%',
},
});
function ButtonAppBar(props) {
const { classes } = props;
const state = {
inputs: {},
};
function toggleDrawer = (open) => () => {
state={
open:false,
};
this.setState({
open,
});
};
const updateInputValue = (evt) => {
state.inputs[evt.target.name] = evt.target.value;
console.log(state.inputs);
};
const handleSubmit = (event) => {
// console.log('handleSubmit', username, password);
if (!event.target.checkValidity()) {
console.log({ displayErrors: true });
}
event.stopPropagation();
event.preventDefault();
return 0;
};
return (
<div className={styles.root}>
<Grid container spacing={8} alignItems="center" justify="center">
<Paper className={classes.paper}>
<AppBar position="static" className={classes.appbar}>
<Toolbar>
<IconButton onClick={this.toggleDrawer(true)} className={classes.menuButton} color="contrast" aria-label="Menu">
<Drawer open={this.state.open} onClose={this.toggleDrawer('open', false)}>
onClick={this.toggleDrawer('open', false)}
</Drawer>
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
<img src={img} alt="React" className={classes.img} /><br />
<form onSubmit={handleSubmit} noValidate>
<TextField id="email" type="email" label="Usuário" className={classes.user} value={state.inputs.username} onChange={evt => updateInputValue(evt)} /><br />
<TextField id="password" type="password" label="Senha" className={classes.senha} value={state.inputs.password} onChange={evt => updateInputValue(evt)} />
<AppBar position="static" className={classes.paper1}>
<Link to="/Orders">
<Button type="submit" color="contrast">Login</Button>
</Link>
</AppBar>
</form>
</Paper>
</Grid>
</div>
);
}
ButtonAppBar.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(ButtonAppBar);
I wonder why this error is happening (I am a beginner in react).
It is a syntax error.
Replace function toggleDrawer = to
const toggleDrawer =