当父组件发生更改时,构造函数不会在子组件中每次都调用。在父级组件中,有一个Developer下拉过滤器。当我们更改开发者下拉菜单时,开发者任务会以开发者任务组件的形式显示。并且我在Developertask组件中添加了另一个Timer组件以显示任务计时器。
timetracker.js
import React, { Component, Fragment } from "react";
import App from "../components/App";
import Header from "../components/Header";
import TodoInlineForm from "../components/TodoInlineForm";
import DeveloperTasks from "../components/DeveloperTasks";
import Typography from "@material-ui/core/Typography";
import FormControl from "@material-ui/core/FormControl";
import InputLabel from "@material-ui/core/InputLabel";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import { withStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import IconButton from "@material-ui/core/IconButton";
import MuiDialogTitle from "@material-ui/core/DialogTitle";
import MuiDialogContent from "@material-ui/core/DialogContent";
import CloseIcon from "@material-ui/icons/Close";
const styles = theme => ({
toolbar: theme.mixins.toolbar,
content: {
flexGrow: 1
},
});
const DialogTitle = withStyles(theme => ({
root: {
borderBottom: `1px solid ${theme.palette.divider}`,
margin: 0,
padding: theme.spacing.unit * 2
},
closeButton: {
position: "absolute",
right: theme.spacing.unit,
top: theme.spacing.unit,
color: theme.palette.grey[500]
}
}))(props => {
const { children, classes, onClose } = props;
return (
<MuiDialogTitle disableTypography className={classes.root}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton
aria-label="Close"
className={classes.closeButton}
onClick={onClose}
>
<CloseIcon />
</IconButton>
) : null}
</MuiDialogTitle>
);
});
class TimeTracker extends React.Component {
state = {
developer: "",
open: false,
fullWidth: true,
maxWidth: "md",
taskid: ""
};
handleChange = name => event => {
this.setState({ open: false });
this.setState({ [name]: event.target.value });
};
handleClose = () => {
this.setState({ open: false });
};
render() {
const { classes, theme } = this.props;
let developerlist;
let todoinlineform;
if (
typeof this.state.developer != "undefined" &&
this.state.developer != ""
) {
developerlist = (
<DeveloperTasks contact_id={this.state.developer} open={false} />
);
}
if (this.state.open && this.state.open === true) {
todoinlineform = (
<TodoInlineForm
open={this.state.open}
modaltitle="Add Todo"
onClose={this.handleClose}
/>
);
}
return (
<App>
<Header />
<main className={classes.content}>
<div className={classes.toolbar} />
<div className={classes.paper}>
<form autoComplete="off">
<FormControl className={classes.formControl}>
<InputLabel htmlFor="developer">Select Developer</InputLabel>
<Select
fullWidth
value={this.state.developer}
onChange={this.handleChange("developer")}
inputProps={{
name: "developer",
id: "developer"
}}
>
<MenuItem value="">
<em>Please Select Developer</em>
</MenuItem>
<MenuItem
key="rigal"
value="b62b9815-b1c7-dc4a-7539-59a68b95ddf5"
>
Rigal Patel
</MenuItem>
<MenuItem
key="ankit"
value="1e1eafa8-ea40-6f43-6541-5c7f539612f2"
>
Ankit Patel
</MenuItem>
</Select>
</FormControl>
<Button
variant="contained"
color="Primary"
className={classes.button}
onClick={this.handleOpen()}
>
Add New Task
</Button>
{todoinlineform}
{developerlist}
</form>
</div>
</main>
</App>
);
}
}
export default withStyles(styles, { withTheme: true })(TimeTracker);
上面的代码仅显示在Developer下拉列表中,当下拉列表更改时,我添加了条件的Developertask组件。
DeveloperTasks.js
import { Mutation, Query } from "react-apollo";
import gql from "graphql-tag";
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
import { withStyles } from "@material-ui/core/styles";
import TaskTimer from "./TaskTimer";
import Note from "./Note";
import getCDTime from "../util/commonfunc";
import Button from "@material-ui/core/Button";
import IconButton from "@material-ui/core/IconButton";
import Paper from "@material-ui/core/Paper";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemText from "@material-ui/core/ListItemText";
import CircularProgress from "@material-ui/core/CircularProgress";
import Avatar from "@material-ui/core/Avatar";
import FormControl from "@material-ui/core/FormControl";
import InputLabel from "@material-ui/core/InputLabel";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import TextField from "@material-ui/core/TextField";
import Dialog from "@material-ui/core/Dialog";
import MuiDialogTitle from "@material-ui/core/DialogTitle";
import MuiDialogContent from "@material-ui/core/DialogContent";
import MuiDialogActions from "@material-ui/core/DialogActions";
import DeleteIcon from "@material-ui/icons/Delete";
import AssignmentIcon from "@material-ui/icons/Assignment";
import NotesIcon from "@material-ui/icons/EventNote";
import AssignmentInd from "@material-ui/icons/AssignmentInd";
import CheckCircleOutline from "@material-ui/icons/CheckCircleOutline";
import CheckCircle from "@material-ui/icons/CheckCircle";
import CloseIcon from "@material-ui/icons/Close";
import Typography from "@material-ui/core/Typography";
import EditIcon from "@material-ui/icons/Edit";
import notify from "../util/notify";
import DateFnsUtils from "@date-io/date-fns";
import {
MuiPickersUtilsProvider,
TimePicker,
DatePicker
} from "material-ui-pickers";
import UserList from "../components/UserList";
import emails from "../components/UserList";
import TodoInlineForm from "../components/TodoInlineForm";
import UserListing from "../components/UserListing";
const ms = require("pretty-ms");
const styles = theme => ({
root: {
width: "100%",
marginTop: theme.spacing(3),
overflowX: "auto"
},
});
const DialogTitle = withStyles(theme => ({
root: {
borderBottom: `1px solid ${theme.palette.divider}`,
margin: 0,
padding: theme.spacing.unit * 2
},
closeButton: {
position: "absolute",
right: theme.spacing.unit,
top: theme.spacing.unit,
color: theme.palette.grey[500]
}
}))(props => {
const { children, classes, onClose } = props;
return (
<MuiDialogTitle disableTypography className={classes.root}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton
aria-label="Close"
className={classes.closeButton}
onClick={onClose}
>
<CloseIcon />
</IconButton>
) : null}
</MuiDialogTitle>
);
});
const DialogContent = withStyles(theme => ({
root: {
margin: 0,
padding: theme.spacing.unit * 2
}
}))(MuiDialogContent);
const DialogActions = withStyles(theme => ({
root: {
borderTop: `1px solid ${theme.palette.divider}`,
margin: 0,
padding: theme.spacing.unit
}
}))(MuiDialogActions);
class DeveloperTasks extends React.Component {
state = {
start_date: new Date(),
end_date: new Date(),
status: "",
task: "",
searchTerm: "",
open: false,
openReAssign: false,
openTodoForm: false,
taskid: "",
userTaskId: "",
value: "",
searchbydate: "",
contact_id: "",
openNotes: ""
};
constructor(props) {
super(props);
this.state = {
start_date: new Date(),
end_date: new Date(),
contact_id: props.contact_id
};
this.searchUpdated = this.searchUpdated.bind(this);
}
handleDateChange = name => date => {
this.setState({ [name]: date });
};
handleChange = name => event => {
this.setState({ [name]: event.target.value });
};
handleClickTodoOpen(taskid) {
this.setState({ taskid: taskid, openTodoForm: true });
}
handleClickSearch = () => {
this.setState({ searchbydate: "1" });
};
render() {
let todoinlineform = "";
let startdate = "";
let enddate = "";
const { classes, contact_id } = this.props;
let currdatetime = getCDTime.getCurrentDateTime();
let shownbutton = {
display: "block"
};
if (
this.state.openTodoForm &&
this.state.openTodoForm === true &&
this.state.taskid != ""
) {
todoinlineform = (
<TodoInlineForm
open={this.state.openTodoForm}
taskid={this.state.taskid}
modaltitle="Edit Todo"
onClose={this.closeTodoPopup}
/>
);
}
if (this.state.searchbydate && this.state.searchbydate != "") {
startdate = this.changeCustomDateFormat(this.state.start_date);
enddate = this.changeCustomDateFormat(this.state.end_date);
}
return contact_id === "" ? (
""
) : (
<Query
query={tasksQuery}
variables={{
contact_id_c: contact_id,
start_due_date: startdate,
end_date: enddate
}}
>
{({ loading, error, data: { Developertasklist } }) => {
if (error) return <p>{error}</p>;
if (loading) return <CircularProgress className={classes.progress} />;
//Filter with task name
if (this.state.task && this.state.task != "") {
Developertasklist = Developertasklist.filter(
developertasklist =>
developertasklist.name
.toLowerCase()
.indexOf(this.state.task.toLowerCase()) != -1
);
}
//Task status wise filter
if (this.state.status && this.state.status != "") {
Developertasklist = Developertasklist.filter(
developertasklist => developertasklist.status == this.state.status
);
}
//Label array for apply class on status label
let labelcolor = [
{ status: "In Progress", class: classes.labelprogress },
{ status: "Completed", class: classes.labelcomplete },
{ status: "On Hold", class: classes.labelonhold },
{ status: "QA Fail", class: classes.labelqafail },
{ status: "Not Started", class: classes.labelnotstated },
{ status: "QA", class: classes.labelqa },
{ status: "QA Passed", class: classes.labelqapassed },
{ status: "Deferred", class: classes.labeldefered }
];
return (
<Fragment>
<br />
<div className={classes.tasklist}>
<div className="picker">
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<DatePicker
label="Start Date"
name="start_date"
value={this.state.start_date}
format="yyyy-MM-dd"
onChange={this.handleDateChange("start_date")}
className={classes.datepadding}
animateYearScrolling
/>
<DatePicker
label="End Date"
name="end_date"
value={this.state.end_date}
format="yyyy-MM-dd"
onChange={this.handleDateChange("end_date")}
className={classes.datepadding}
animateYearScrolling
/>
</MuiPickersUtilsProvider>
<Button
type="button"
variant="contained"
color="primary"
className={classes.button}
onClick={event => {
this.handleClickSearch();
}}
>
Search
</Button>
<Button
variant="contained"
color="secondary"
className={classes.button}
onClick={event => {
this.handleClickReset();
}}
>
Reset
</Button>
</div>
<FormControl className={classes.formControl}>
<InputLabel htmlFor="status-simple">Status</InputLabel>
<Select
value={this.state.status}
onChange={this.handleChange("status")}
className={classes.elementpadding}
inputProps={{
name: "status",
id: "status"
}}
>
<MenuItem value="">
<em>Please Select</em>
</MenuItem>
<MenuItem value="Not Started">Not Started</MenuItem>
<MenuItem value="In Progress">In Progress</MenuItem>
<MenuItem value="On Hold">On Hold</MenuItem>
<MenuItem value="Deferred">Deferred</MenuItem>
<MenuItem value="Completed">Completed</MenuItem>
<MenuItem value="QA">QA</MenuItem>
<MenuItem value="QA Passed">QA Passed</MenuItem>
<MenuItem value="QA Fail">QA Fail</MenuItem>
</Select>
</FormControl>
<TextField
id="standard-name"
label="Task"
className={classes.textField}
value={this.state.task}
onChange={this.handleChange("task")}
/>
</div>
<div className={classes.tasklist}>
<Paper className={classes.listroot}>
<List className={classes.listroot}>
{Developertasklist.map((task, index) => {
let statusLabel = labelcolor.filter(
obj => obj.status == task.status
);
let completeStatusClass = classes.hideelement;
let pendingStatusClass = "";
let hidetimer = "";
if (task.status === "Completed") {
pendingStatusClass = classes.hideelement;
hidetimer = "hide";
completeStatusClass = "";
}
if (statusLabel.length > 0)
statusLabel = statusLabel[0].class;
let currentdatetime = new Date(task.due_date);
let start_time = new Date(task.start_due_date);
let time_diff = Math.abs(currentdatetime - start_time) / 1000;
return (
<ListItem key={index} divider="true">
<div className={classes.taskwidth}>
<Avatar>
<AssignmentIcon />
</Avatar>
<ListItemText
primary={
<React.Fragment>
{task.name} - {task.due_date}
</React.Fragment>
}
secondary={
<React.Fragment>
<Typography
component="span"
className={[classes.label, statusLabel]}
color="textPrimary"
>
{task.status}
</Typography>
<Typography variant="caption" gutterBottom className={[classes.etime]}>
Estimated Hours: { time_diff / 3600 } Hours
</Typography>
</React.Fragment>
}
/>
</div>
<div className={classes.timerwidth}>
<div>
<TaskTimer
developerlist={task}
hidetimer={hidetimer}
alltasklist={Developertasklist}
//hidetimer={this.state.disabledList[n]}
//disableOtherTimersCallback={getDisableOtherTimersCallback()}
/>
</div>
</div>
<div className={classes.width5}>
<EditIcon
className={classes.icon}
onClick={event => {
this.handleClickTodoOpen(task.id);
}}
/>
</div>
<div className={classes.width5}>
<Mutation mutation={COMPLETE_TASK_OPERATIONS}>
{(todo_operations, { loading, error }) => (
<CheckCircleOutline
className={[pendingStatusClass, classes.icon]}
onClick={event => {
let confirmcomplete = window.confirm(
"Are you sure you want to complete this Task?"
);
confirmcomplete &&
todo_operations({
variables: {
id: task.id,
actual_due_date: currdatetime,
status: "Completed"
}
}) &&
notify.notify(
"success",
"Task Completed successfully!"
);
}}
/>
)}
</Mutation>
<CheckCircle
className={[
classes.icon,
classes.completeIcon,
completeStatusClass
]}
/>
</div>
<div className={classes.width5}>
<div className={pendingStatusClass}>
<AssignmentInd
className={classes.icon}
onClick={event => {
this.handleClickDialogOpen(task.id);
}}
/>
<UserListing
classes={{
paper: classes.userlistingstyle
}}
//open={this.state.openReAssign}
open={this.state.userTaskId === task.id}
onClose={this.handleDialogClose}
//value={this.state.value}
value={task.contact_id_c}
userTaskId={this.state.userTaskId}
/>
</div>
</div>
<div className={classes.width5}>
<NotesIcon
className={classes.icon}
onClick={this.handleClickOpen(task.id)}
/>
<Dialog
onClose={this.handleClose}
aria-labelledby="customized-dialog-title"
open={this.state.openNotes === task.id}
>
<DialogTitle
id="customized-dialog-title"
onClose={this.handleClose}
>
Notes
</DialogTitle>
<DialogContent>
<Note
todoid={task.id}
closeNote={this.handleClose}
/>
</DialogContent>
</Dialog>
</div>
<div className={classes.width5}>
<Mutation mutation={DELETE_TODO}>
{(todo_operations, { loading, error }) => (
<DeleteIcon
className={classes.icon}
aria-label="Delete"
onClick={event => {
let confirmdelete = window.confirm(
"Are you sure you want to delete this Task?"
);
confirmdelete &&
todo_operations({
variables: {
id: task.id,
deleted: "1"
}
}) &&
notify.notify(
"success",
"Record deleted successfully!"
);
}}
/>
)}
</Mutation>
</div>
</ListItem>
);
})}
</List>
</Paper>
</div>
{todoinlineform}
</Fragment>
);
}}
</Query>
);
}
}
export default withStyles(styles, { withTheme: true })(DeveloperTasks);
The above components are display developer tasks based on developer dropdown value. I have added Timer Component for task time tracking.
I have passed the task object in the Timer component and get task object value in timer components as Props but not getting props value in the constructor because when we change developer Dropdown in starting (two-three timer construct call so I get task object in props but after that constructor not call (seems like data get form cache).
```
import { Mutation, Query } from "react-apollo";
import gql from "graphql-tag";
const React = require("react");
const ms = require("pretty-ms");
import PropTypes from "prop-types";
import { withStyles } from "@material-ui/core/styles";
import StartIcon from "@material-ui/icons/PlayCircleFilled";
import StopIcon from "@material-ui/icons/Stop";
import getCDTime from "../util/commonfunc";
enter code here
const styles = theme => ({
button: {
margin: theme.spacing.unit
},
});
class TaskTimer extends React.Component {
constructor(props) {
const total_time = !props.developerlist.dtask_total_time
? parseInt(0)
: parseInt(props.developerlist.dtask_total_time);
let statetime = total_time;
let stateison = false;
super(props);
if (props.developerlist.time_tracking_flag === "yes") {
let currentdatetime = new Date(getCDTime.getCurrentDateTime());
let start_time = new Date(props.developerlist.dtask_start_time);
let time_diff = Math.abs(currentdatetime - start_time) / 1000;
statetime = time_diff + total_time;
stateison = true;
this.state = {
time: statetime,
isOn: stateison
};
this.startTimer();
}
this.state = {
time: statetime,
isOn: stateison
};
this.startTimer = this.startTimer.bind(this);
this.stopTimer = this.stopTimer.bind(this);
}
startTimer(next) {
this.setState({
isOn: true,
time: this.state.time
});
this.timer = setInterval(
() =>
this.setState({
time: parseInt(this.state.time + 1)
}),
1000
);
}
stopTimer() {
this.state.time = parseInt(this.state.time);
this.setState({ isOn: false });
clearInterval(this.timer);
}
getDisableOtherTimersCallback() {
alert("here");
}
render() {
let totalTaskTime = parseInt(this.state.time) * 1000;
const { classes, theme } = this.props;
let currdatetime = getCDTime.getCurrentDateTime();
let currentDate = new Date(currdatetime).toDateString();
let taskStateDate = new Date(
this.props.developerlist.dtask_start_time
).toDateString();
let dailyhours =
currentDate != taskStateDate
? this.props.developerlist.dtask_total_time
: 0;
return (
<div>
<div className={classes.clock}>{ms(totalTaskTime)}</div>
{start}
{stop}
</div>
);
}
}
export default withStyles(styles, { withTheme: true })(TaskTimer);
```
Anyone, please suggest a possible solution to fix this issue.