首页 > 解决方案 > 单个组件更改时如何更新其他组件

问题描述

我正在创建计时器组件并实现每个任务。因此,当我为单个任务启动计时器时,其他任务计时器将被禁用或隐藏。我正在尝试在启动计时器上禁用其他计时器组件,但它只为我提供了当前组件的值。那么当我启动一个计时器时如何更新所有组件呢?

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 Popover from "@material-ui/core/Popover";

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 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";

const ms = require("pretty-ms");

//Kanban Quearies
export const tasksQuery = gql`
  query Developertasklist($contact_id_c: String) {
    Developertasklist(contact_id_c: $contact_id_c) {
      id
      name
      due_date
      dtask_start_time
      time_tracking_flag
      dtask_total_time
      status
    }
  }
`;
//Delete Task Mutation
export const DELETE_TODO = gql`
  mutation todo_operations($id: String, $deleted: String) {
    todo_operations(id: $id, deleted: $deleted) {
      id
    }
  }
`;
//Complete Task Mutation
const COMPLETE_TASK_OPERATIONS = gql`
  mutation todo_operations(
    $id: String
    $status: String
    $actual_due_date: String
  ) {
    todo_operations(
      id: $id
      status: $status
      actual_due_date: $actual_due_date
    ) {
      id
    }
  }
`;

const styles = theme => ({
  root: {
    width: "100%",
    marginTop: theme.spacing(3),
    overflowX: "auto"
  },
  icon: {
    margin: theme.spacing.unit,
    fontSize: 20
  },
  button: {
    margin: theme.spacing.unit
  },
  listroot: {
    width: "100%",
    minWidth: 900,
    backgroundColor: theme.palette.background.paper
  },
  tasklist: {
    marginTop: 30
  },
  taskwidth: {
    width: "55%",
    display: "inline-flex"
  },
  timerwidth: {
    width: "25%"
  },
  width5: {
    width: "5%"
  },
  margin: {
    margin: theme.spacing.unit
  },
  input: {
    display: "none"
  },
  datepadding: {
    "padding-right": "10px;",
    width: "17%"
  },
  formControl: {
    minWidth: 120
  },
  elementpadding: {
    "padding-right": "10px;"
  },
  completeIcon: {
    color: "Green"
  },
  popover: {
    pointerEvents: "none"
  },
  label: {
    display: "inline",
    padding: ".2em .6em .3em",
    "font-size": "75%",
    "font-weight": "700",
    "line-height": 1,
    color: "#fff",
    "text-align": "center",
    "white-space": "nowrap",
    "vertical-align": "baseline",
    "border-radius": ".25em"
  },
  labelcomplete: {
    "background-color": "#5cb85c"
  },
  labelprogress: {
    "background-color": "#5bc0de"
  },
  labelonhold: {
    "background-color": "#d9534f"
  },
  labelqafail: {
    "background-color": "#d9534f"
  },
  labelnotstated: {
    "background-color": "#777"
  },
  labelqa: {
    "background-color": "#337ab7"
  },
  labelqapassed: {
    "background-color": "#777"
  },
  labeldefered: {
    "background-color": "#f0ad4e"
  },
  hideelement: {
    display: "none"
  },
  showelement: {
    display: "block"
  }
});

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,
    anchorEl: null,
    selectedValue: emails[1],
    openreport: false,
    openTodoForm: false,
    taskid: ""
  };
  constructor(props) {
    super(props);
    this.searchUpdated = this.searchUpdated.bind(this);
  }
  handleDateChange = name => date => {
    this.setState({ [name]: date });
  };

  handleChange = name => event => {
    this.setState({ [name]: event.target.value });
  };

  handleClickOpen = name => event => {
    this.setState({
      open: true
    });
  };
  handleClose = () => {
    this.setState({ open: false });
  };

  handleClickDialogOpen = () => {
    this.setState({ openreport: true });
  };

  handleDialogClose = value => {
    this.setState({ selectedValue: value, openreport: false });
  };

  searchUpdated(term) {
    this.setState({ searchTerm: term });
  }

  handlePopoverOpen = event => {
    this.setState({ anchorEl: event.currentTarget });
  };

  handlePopoverClose = () => {
    this.setState({ anchorEl: null });
  };

  handleClickTodoOpen(taskid) {
    this.setState({ taskid: taskid, openTodoForm: true });
  }

  componentWillReceiveProps(newProps) {
    this.setState({ openTodoForm: newProps.open });
  }

  render() {
    let todoinlineform = "";
    const { classes, contact_id } = this.props;

    const { anchorEl } = this.state;
    const open = Boolean(anchorEl);
    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"
        />
      );
    }

    return contact_id === "" ? (
      ""
    ) : (
      <Query query={tasksQuery} variables={{ contact_id_c: contact_id }}>
        {({ 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="submit"
                    variant="contained"
                    color="primary"
                    className={classes.button}
                  >
                    Search
                  </Button>
                  <Button
                    variant="contained"
                    color="secondary"
                    className={classes.button}
                  >
                    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>
                &nbsp;
                <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;

                      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>
                                </React.Fragment>
                              }
                            />
                          </div>
                          <div className={classes.timerwidth}>
                            <div>
                              <TaskTimer
                                developerlist={task}
                                hidetimer={hidetimer}
                              />
                            </div>
                          </div>
                          <div className={classes.width5}>
                            <EditIcon
                              onClick={event => {
                                this.handleClickTodoOpen(task.id);
                              }}
                            />
                          </div>
                          <div className={classes.width5}>
                            <Mutation mutation={COMPLETE_TASK_OPERATIONS}>
                              {(todo_operations, { loading, error }) => (
                                <CheckCircleOutline
                                  className={pendingStatusClass}
                                  aria-owns={
                                    open ? "mouse-over-popover" : undefined
                                  }
                                  aria-haspopup="true"
                                  onMouseEnter={this.handlePopoverOpen}
                                  onMouseLeave={this.handlePopoverClose}
                                  onClick={event => {
                                    todo_operations({
                                      variables: {
                                        id: task.id,
                                        actual_due_date: currdatetime,
                                        status: "Completed"
                                      }
                                    });
                                  }}
                                />
                              )}
                            </Mutation>
                            <Popover
                              id="mouse-over-popover"
                              className={classes.popover}
                              classes={{
                                paper: classes.paper
                              }}
                              open={open}
                              anchorEl={anchorEl}
                              anchorOrigin={{
                                vertical: "bottom",
                                horizontal: "left"
                              }}
                              transformOrigin={{
                                vertical: "top",
                                horizontal: "left"
                              }}
                              onClose={this.handlePopoverClose}
                              disableRestoreFocus
                            >
                              <Typography>Mark as completed.</Typography>
                            </Popover>
                            <CheckCircle
                              className={[
                                classes.completeIcon,
                                completeStatusClass
                              ]}
                            />
                          </div>
                          <div className={classes.width5}>
                            <div className={pendingStatusClass}>
                              {/* <Typography variant="subtitle1">
                                Selected: {this.state.selectedValue}
                              </Typography> */}
                              <AssignmentInd
                                onClick={this.handleClickDialogOpen}
                              />
                              <UserList
                                selectedValue={this.state.selectedValue}
                                open={this.state.openreport}
                                onClose={this.handleDialogClose}
                              />
                            </div>
                          </div>
                          <div className={classes.width5}>
                            <NotesIcon onClick={this.handleClickOpen()} />
                            <Dialog
                              onClose={this.handleClose}
                              aria-labelledby="customized-dialog-title"
                              open={this.state.open}
                            >
                              <DialogTitle
                                id="customized-dialog-title"
                                onClose={this.handleClose}
                              >
                                Notes
                              </DialogTitle>
                              <DialogContent>
                                <Note />
                              </DialogContent>
                            </Dialog>
                          </div>
                          <div className={classes.width5}>
                            <Mutation mutation={DELETE_TODO}>
                              {(todo_operations, { loading, error }) => (
                                <DeleteIcon
                                  aria-label="Delete"
                                  onClick={event => {
                                    todo_operations({
                                      variables: {
                                        id: task.id,
                                        deleted: "1"
                                      }
                                    });
                                  }}
                                />
                              )}
                            </Mutation>
                          </div>
                        </ListItem>
                      );
                    })}
                  </List>
                </Paper>
              </div>
              {todoinlineform}
            </Fragment>
          );
        }}
      </Query>
    );
  }
}

export default withStyles(styles, { withTheme: true })(DeveloperTasks);

任务定时器.js

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";

//Start timer mutation
const TODO_OPERATIONS = gql`
  mutation todo_operations(
    $id: String
    $status: String
    $dtask_start_time: String
    $time_tracking_flag: String
    $developer_daily_hours: String
    $is_task_started: String
    $actual_start_date: String
  ) {
    todo_operations(
      id: $id
      status: $status
      dtask_start_time: $dtask_start_time
      time_tracking_flag: $time_tracking_flag
      developer_daily_hours: $developer_daily_hours
      is_task_started: $is_task_started
      actual_start_date: $actual_start_date
    ) {
      id
    }
  }
`;
//Stop timer mutation
const STOP_TIMER = gql`
  mutation todo_operations(
    $id: String
    $dtask_stop_time: String
    $dtask_total_time: String
    $time_tracking_flag: String
  ) {
    todo_operations(
      id: $id
      dtask_stop_time: $dtask_stop_time
      dtask_total_time: $dtask_total_time
      time_tracking_flag: $time_tracking_flag
    ) {
      id
    }
  }
`;

const styles = theme => ({
  button: {
    margin: theme.spacing.unit
  },
  stopbutton: {
    margin: theme.spacing.unit,
    color: "Red"
  },
  input: {
    display: "none"
  },
  clock: {
    color: "Green",
    fontWeight: "700",
    fontSize: "15px"
  },
  hideelement: {
    display: "none"
  },
  timerClass: {
    display: "none"
  }
});

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,
      timerClass: "test"
    };

    this.startTimer = this.startTimer.bind(this);
    this.stopTimer = this.stopTimer.bind(this);
  }

  startTimer(next) {
    this.setState({
      isOn: true,
      time: this.state.time,
      timerClass: "abc"
    });

    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);
  }

  render() {
    let totalTaskTime = parseInt(this.state.time) * 1000;
    const { classes, theme } = this.props;
    let hideTimerClass =
      this.props.hidetimer === "hide" ? classes.hideelement : "";
    let currdatetime = getCDTime.getCurrentDateTime();
    let start =
      (this.state.time == 0 || this.state.time > 0) && this.state.isOn == 0 ? (
        <Mutation mutation={TODO_OPERATIONS}>
          {(todo_operations, { loading, error }) => (
            <StartIcon
              variant="contained"
              color="primary"
              className={[
                classes.button,
                hideTimerClass,
                this.state.timerClass
              ]}
              //className={this.state.timerClass}
              onClick={event => {
                this.startTimer();
                todo_operations({
                  variables: {
                    id: this.props.developerlist.id,
                    status: "In Progress",
                    dtask_start_time: currdatetime,
                    time_tracking_flag: "yes",
                    developer_daily_hours: dailyhours,
                    is_task_started: "yes",
                    actual_start_date: currdatetime
                  }
                });
              }}
            />
          )}
        </Mutation>
      ) : null;

    let stop =
      this.state.isOn && this.state.isOn == 1 ? (
        <Mutation mutation={STOP_TIMER}>
          {(todo_operations, { loading, error }) => (
            <StopIcon
              variant="contained"
              className={[classes.stopbutton, hideTimerClass]}
              disabled={true}
              onClick={event => {
                this.stopTimer();
                todo_operations({
                  variables: {
                    id: this.props.developerlist.id,
                    dtask_stop_time: currdatetime,
                    dtask_total_time: this.state.time,
                    time_tracking_flag: "stop"
                  }
                });
              }}
            />
          )}
        </Mutation>
      ) : null;

    return (
      <div>
        <div className={classes.clock}>{ms(totalTaskTime)}</div>
        {start}
        {stop}
      </div>
    );
  }
}

export default withStyles(styles, { withTheme: true })(TaskTimer);

标签: reactjs

解决方案


如果我正确理解了您的问题,那么当某个事件发生时,您正试图在姐妹组件中进行更新。

我相信最好的方法是在你的父组件(DeveloperTasks)中有一个状态,它保存每个计时器是否应该被禁用。然后将回调传递给每个计时器,该计时器将以您正在寻找的方式更新禁用列表。

我想象这样的回调会起作用的方式是:

function getDisableOtherTimersCallback(timerNum) {
    return timerNum => {
       return this.state.setState({disabledList: disabledList.map((value, index) => index == timerNum)})
        // this line just goes through and makes sure that the index corresponding to timerNum is the only one that's true
    }; 
}

然后,当您为计时器 n 渲染组件时,您将传递为计时器 n 获得的计时器。

<TaskTimer
       developerlist={task}
       hidetimer={this.state.disabledList[n]}
       disableOtherTimersCallback={getDisableOtherTimersCallback(n)}
       />

然后,每当您想禁用其他计时器时,您都会调用此方法并完成!

(另请注意,您现在可以使用 disabledList 来显示或隐藏每个计时器!)

(另请注意,您需要将 disabledList 添加到您的状态并以您正在寻找的方式启动它)

希望这可以帮助!


推荐阅读