How to update other component when change in single component - reactjs

I am creating timer component and implement with every task. So when I start my timer for a single task then other task timer will be disabled or hidden. I am trying to disable other timer component on start timer but it gives me only value for current component. So how can I update all components when I start a single timer?
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>
<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);
TaskTimer.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);

if I'm understanding your problem correctly you are trying to cause an update in sister components when a certain event happens.
I believe that the best way to do this would be to have a state in your parent component (DeveloperTasks) that holds whether or not each timer should be disabled. Then pass a callback into each timer that would update the disabled list in the way that you're looking for.
The way I'm imagining such a callback would work is:
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
};
}
Then when you render your component for timer n you would pass in the timer you get for timer n.
<TaskTimer
developerlist={task}
hidetimer={this.state.disabledList[n]}
disableOtherTimersCallback={getDisableOtherTimersCallback(n)}
/>
Then whenever you want to disable the other timers you would call this method and your done!
(Also note that you can now use the disabledList to show or hide each timer!)
(Also also note that you need to add the disabledList to your state and initiate it in the way you are looking for)
Hope this helps!

Related

How to change react-select option automatically after 5 seconds

I have created a dropdown menu in react using the react-select package, The dropdown menu is working as as expected, now I want to change the option value automatically after some seconds. After some seconds it should select another option from the option array, which option I want from the option array option.
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import Select, { components } from "react-select";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faCircle, faMinusCircle } from "#fortawesome/fontawesome-free-solid";
import "./presence.css";
function Presence() {
const [presence, setPresence] = useState({
value: "unavailable",
label: "Offline",
icon: <FontAwesomeIcon icon={faCircle} color="gray" />,
});
console.log(presence);
const client = useSelector((state) => state.client.client);
const handleChange = (selectedOption) => {
setPresence(selectedOption.label);
console.log(`Option selected:`, selectedOption);
};
const { Option, SingleValue } = components;
const CustomSelectOption = (props) => (
<Option {...props}>
<div style={{ display: "inline-block", marginRight: "5%" }}>
{props.data.icon}{" "}
</div>
<div style={{ display: "inline-block" }}> {props.data.label}</div>
</Option>
);
const ValueOption = (props) => (
<SingleValue {...props}>
<span style={{ marginRight: "8%" }}> {props.data.icon}</span>
<span>{props.data.label}</span>
</SingleValue>
);
const options = [
{
value: "chat",
label: "Available",
icon: <FontAwesomeIcon icon={faCircle} color="#5cd068" />,
},
{
value: "xa",
label: "Appear Away",
icon: <FontAwesomeIcon icon={faCircle} color="orange" />,
},
{
value: "away",
label: "Be Right Back",
icon: <FontAwesomeIcon icon={faCircle} color="orange" />,
},
{
value: "dnd",
label: "Do not Disturb",
icon: <FontAwesomeIcon icon={faMinusCircle} color="red" />,
},
{
value: "unavailable",
label: "Offline",
icon: <FontAwesomeIcon icon={faCircle} color="gray" />,
},
];
const style = {
control: (base) => ({
...base,
border: 0,
boxShadow: "none",
}),
placeholder: (base) => ({
...base,
fontSize: "1em",
fontWeight: 600,
}),
};
const DropdownIndicator = (props) => {
return (
components.DropdownIndicator && (
<components.DropdownIndicator {...props}>
<FontAwesomeIcon
icon={props.selectProps.menuIsOpen ? "caret-up" : "caret-down"}
/>
</components.DropdownIndicator>
)
);
};
return (
<Select
styles={style}
name="presence"
clearable={false}
placeholder={"Choose"}
onChange={handleChange}
options={options}
classNamePrefix="select"
components={{
Option: CustomSelectOption,
SingleValue: ValueOption,
DropdownIndicator: DropdownIndicator,
IndicatorSeparator: () => null,
}}
/>
);
}
export default Presence;
If you want to execute some code after the first render only, use useEffect(() => {}, []) (notice the empty array), to execute once after 5 seconds, call the setTimeout in the callback like this:
useEffect(() => {
setTimeout(() => {
handlerChange(options.find(o => o.value === 'chat'));
}, 5000);
}, []);
You have no value defined on your <Select>
You have no getOptionValue or getOptionLabel defined (though it's probably using the defaults)
If presence were your value, all it would require is the selectedOption.value
Since presence appears to be defaulted to unavailable, then your initial value would never be away
You are rebuilding a lot of stuff on every rerender of Presence, which will force rerender your <Select> every time.
You are defaulting your presence to "unavailable", so it would never start as "away"
Maybe you're saying you will change the value by loading some record on component load. In that situation, you would put your window.setTimeout in that method. I put some notes on that in the comments below.
Refactored Presence.js:
import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import Select, { components } from "react-select";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faCircle, faMinusCircle } from "#fortawesome/fontawesome-free-solid";
import "./presence.css";
const { Option, SingleValue, DropdownIndicator } = components;
const CustomSelectOption = (props) => {
const {
data: { icon: Icon, label }
} = props;
return (
<Option {...props}>
<div style={{ display: "inline-block", marginRight: "5%" }}>
<Icon />{" "}
</div>
<div style={{ display: "inline-block" }}> {label}</div>
</Option>
);
};
const ValueOption = (props) => {
const {
data: { icon: Icon, label }
} = props;
return (
<SingleValue {...props}>
<span style={{ marginRight: "8%" }}>
{" "}
<Icon />
</span>
<span>{label}</span>
</SingleValue>
);
};
const Indicator = (props) => {
const icon = props.selectProps.menuIsOpen ? "caret-up" : "caret-down";
return (
<DropdownIndicator {...props}>
<FontAwesomeIcon
icon={icon}
/>
</DropdownIndicator>
);
};
const Separator = () => null;
const options = [
{
value: "chat",
label: "Available",
icon: <FontAwesomeIcon icon={faCircle} color="#5cd068" />
},
{
value: "xa",
label: "Appear Away",
icon: <FontAwesomeIcon icon={faCircle} color="orange" />
},
{
value: "away",
label: "Be Right Back",
icon: <FontAwesomeIcon icon={faCircle} color="orange" />
},
{
value: "dnd",
label: "Do not Disturb",
icon: <FontAwesomeIcon icon={faMinusCircle} color="red" />
},
{
value: "unavailable",
label: "Offline",
icon: <FontAwesomeIcon icon={faCircle} color="gray" />
}
];
const style = {
control: (base) => ({
...base,
border: 0,
boxShadow: "none"
}),
placeholder: (base) => ({
...base,
fontSize: "1em",
fontWeight: 600
})
};
const getLabel = (option) => option.label;
const getValue = (option) => option.value;
function Presence() {
const [presence, setPresence] = useState("unavailable");
console.log(presence);
const client = useSelector((state) => state.client.client);
/**
* Let's say you include some other process to 'set' `presence`,
* like loading some record or something. The you could 'init'ialize
* your component by then adding your timeout. You can't really do
* this alone in a `useEffect`, as you defaulted the value, which
* would trigger your effect.
*
window.setTimeout(() => {
// make sure to check that current value again, in case
// someone changed it in that 5 seconds
setPresence(prev => prev === 'away' ? 'chat' : prev);
}, 5000);
*/
const handleChange = (selectedOption) => {
setPresence(selectedOption?.value);
console.log(`Option selected:`, selectedOption);
};
return (
<Select
styles={style}
name="presence"
isClearable={false}
placeholder={"Choose"}
onChange={handleChange}
options={options}
classNamePrefix="select"
components={{
Option: CustomSelectOption,
SingleValue: ValueOption,
DropdownIndicator: Indicator,
IndicatorSeparator: Separator
}}
getOptionLabel={getLabel}
getOptionValue={getValue}
value={presence}
/>
);
}
export default Presence;

React Maximum update depth exceeded - can't spot the loop

I'm a very new dev working on legacy code, so I'll apologize now. But I'm getting this error when I try to "add a rule" to an array of rules. I did try googling this, and understand I am likely looping a setState, but I can't spot it. Below is the code I believe is relevant, I am not using useEffect, and I don't think I'm calling the function directly. Any help would be appreciated, thanks.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import {
Grid,
MenuItem,
TextField,
Typography,
IconButton,
InputAdornment,
Select,
} from '#material-ui/core';
import ArrowIcon from '#material-ui/icons/ArrowDropDown'
import AddIcon from '#material-ui/icons/Add';
import DeleteIcon from '#material-ui/icons/Clear';
import DragHandle from '#material-ui/icons/DragHandle';
import {
DropTarget,
DragSource,
} from 'react-dnd';
import registry from "../registry";
import {Can} from '../utils';
let schema = registry.actions.RUN.configDef;
const styles = theme => ({
row: {
'&:nth-of-type(even)': {
backgroundColor: theme.palette.background.default,
},
borderTop: '1px solid',
borderColor: theme.palette.grey[400]
},
header: {
fontWeight: 'normal',
textAlign: 'center'
},
border: {
border: '1px solid',
borderColor: theme.palette.grey[400],
borderRadius: '4px',
padding: '8px',
overflow: 'hidden'
},
add: {
color: theme.palette.primary.main
},
clear: {
color: theme.palette.error.main
},
textField: {
margin: '8px'
},
rightJustifiedText: {
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
margin: '8px'
},
container: {
width: '100%'
},
iconContainer: {
padding: '0px 8px 0px 8px'
},
flexContainer: {
display: 'flex',
alignItems: 'center'
}
});
function CustomArrowIcon(props) {
return (<ArrowIcon {...props} style={{position: 'absolute', left: 0}}/>)
}
function RowContent({id, onRemove, children, connectDropTarget, connectDragSource, isDragging, classes, ability, accountNumber}) {
const opacity = isDragging ? 0.5 : 1;
return (<Grid className={classes.row} item xs={12} key={id}>
{connectDropTarget(
<span className={classes.flexContainer} style={{opacity}}>
{connectDragSource(
<span className={classes.iconContainer}>
<Can I={'update'} a={'serviceActionProfile'} where={'accountNumber'} equals={accountNumber} ability={ability}>
{can => (
<IconButton disabled={!can}>
<DragHandle/>
</IconButton>
)}
</Can>
</span>
)}
{children}
<span className={classes.iconContainer}>
<Can I={'update'} a={'serviceActionProfile'} where={'accountNumber'} equals={accountNumber} ability={ability}>
{can => (
<IconButton disabled={!can} onClick={onRemove}>
<DeleteIcon className={can ? classes.clear : undefined}/>
</IconButton>
)}
</Can>
</span>
</span>
)}
</Grid>);
}
const DraggableRow = DropTarget('row', {
hover: (props, monitor) => {
const monitorItem = monitor.getItem();
const dragIndex = monitorItem.index;
const hoverIndex = props.index;
if (dragIndex === hoverIndex) return null;
let offsetY = monitor.getClientOffset().y;
// When a short item is swapped with a tall item the short item will continue to hover the tall item
// This would normally cause the two to be swapped again, and again, and again...
// Dont re-swap the items until the mouse moves in the other direction
if (dragIndex < hoverIndex && offsetY <= monitorItem.offsetY) {
monitorItem.offsetY = offsetY;
return
}
if (dragIndex > hoverIndex && offsetY >= monitorItem.offsetY) {
monitorItem.offsetY = offsetY;
return
}
props.moveRow(dragIndex, hoverIndex);
monitorItem.index = hoverIndex;
monitorItem.offsetY = offsetY;
},
}, (connect) => {
return {
connectDropTarget: connect.dropTarget()
};
})(DragSource('row', {
beginDrag: (props, monitor) => {
return { index: props.index, offsetY: monitor.getClientOffset().y };
},
}, (connect, monitor) => {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
};
})(withStyles(styles)(RowContent)));
class Modify extends React.Component {
constructor(props) {
super(props);
let now = Date.now();
this.state = {
rules: (props.configValues.rules || []).slice().sort((a,b) => a.index-b.index).map((rule, index) => {
return {
...rule,
toIsValid: validateToFrom('', rule.to.path.trim(), props.serviceActionProfile.jsonataVersion),
fromIsValid: validateToFrom(rule.from.type, rule.from.value.trim(), props.serviceActionProfile.jsonataVersion),
id: ++now,
index
};
})
};
}
componentDidMount() {
this._notifyParent();
}
onChangeAction = (index, event) => {
let action = event.target.value;
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules[index].action = action;
return {
rules: newRules
};
}, this._notifyParent);
};
onChangeToPrefix = (index, event) => {
let prefix = event.target.value;
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules[index].to.prefix = prefix;
return {
rules: newRules
};
}, this._notifyParent);
};
onChangeToPath = (index, event) => {
let path = event.target.value;
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules[index].to.path = path;
newRules[index].toIsValid = validateToFrom('', path.trim(), this.props.serviceActionProfile.jsonataVersion);
return {
rules: newRules
};
}, this._notifyParent);
};
onChangeFromType = (index, event) => {
let type = event.target.value;
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules[index].from.type = type;
newRules[index].fromIsValid = validateToFrom(type, newRules[index].from.value.trim(), this.props.serviceActionProfile.jsonataVersion);
return {
rules: newRules
};
}, this._notifyParent);
};
onChangeFromValue = (index, event) => {
let value = event.target.value;
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules[index].from.value = value;
newRules[index].fromIsValid = validateToFrom(newRules[index].from.type, value.trim(), this.props.serviceActionProfile.jsonataVersion);
return {
rules: newRules
};
}, this._notifyParent);
};
onRemoveRule = (index) => {
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules.splice(index, 1);
newRules.forEach((map, index) => {
map.index = index;
});
return {
rules: newRules
}
}, this._notifyParent);
};
addRule = () => {
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules.push({
id: Date.now(),
index: newRules.length,
action: 'Set',
to: {
prefix: 'local.',
path: ''
},
from: {
type: 'local.',
value: ''
}
});
return {
rules: newRules
};
}, this._notifyParent);
};
_notifyParent() {
let config = {
rules: this.state.rules.map(rule => {
let clone = {
...rule
};
delete clone.id;
delete clone.toIsValid;
delete clone.fromIsValid;
return clone;
})
};
const profile = {
...this.props.serviceActionProfile, // Add any additional properties that might be on the profile and used for validation like the jsonataVersion.
name: "PLACEHOLDER",
action: 'RUN',
documents: {},
configValues: config, // rules
};
const validation = validate(profile);
this.props.onChange({}, config, validation.validOverall);
}
moveRow = (from, to) => {
this.setState(oldState => {
let newRules = oldState.rules.slice();
let moving = newRules.splice(from, 1)[0];
newRules.splice(to, 0, moving);
newRules.forEach((map, index) => {
map.index = index;
});
return {
rules: newRules
}
}, this._notifyParent);
};
render() {
const { classes, ability, accountNumber } = this.props;
const textFieldMargin = 'dense';
return (
<div className={classes.border}>
<Can I={'update'} a={'serviceActionProfile'} where={'accountNumber'} equals={accountNumber} ability={ability}>
{can => (
<React.Fragment>
<Grid container
spacing={16}
direction={"column"}>
<Grid item xs={12} container alignItems={'center'}>
<Grid item xs={1}>
<span className={classes.iconContainer}>
<IconButton className={classes.add} disabled={!can} onClick={this.addRule}>
<AddIcon/>
</IconButton>
</span>
</Grid>
<Grid item xs={10}>
<Typography component={'div'} className={classes.header}>
Rules
</Typography>
</Grid>
</Grid>
{this.state.rules.slice().sort((a,b) => a.index-b.index).map(({action, to, toIsValid, from, fromIsValid, id}, index) => {
return (<DraggableRow key={id}
index={index}
onRemove={this.onRemoveRule.bind(this, index)}
moveRow={can ? this.moveRow : ()=>{}}
ability={ability}
accountNumber={accountNumber}>
<div className={classes.container}>
<span className={classes.flexContainer}>
<TextField select
className={classes.textField}
variant={'outlined'}
margin={textFieldMargin}
style={{width: '150px'}}
value={action}
fullWidth
disabled={!can}
onChange={this.onChangeAction.bind(this, index)}>
{schema.properties.rules.items.properties.action.enum.map(value => {
return (<MenuItem key={value} value={value}>{value}</MenuItem>)
})}
</TextField>
<TextField variant={'outlined'}
label={'Field'}
className={classes.textField}
margin={textFieldMargin}
value={to.path}
fullWidth
disabled={!can}
error={!toIsValid}
onChange={this.onChangeToPath.bind(this, index)}
inputProps={{style: {paddingLeft: '4px'}}}
InputProps={{
style: {width: 'calc(100% - 14px)'},
startAdornment: (
<InputAdornment>
<Select value={to.prefix}
disabled={!can}
onChange={this.onChangeToPrefix.bind(this, index)}
IconComponent={CustomArrowIcon}
SelectDisplayProps={{
style: {paddingLeft: '24px', paddingRight: '0px'}
}}>
{schema.properties.rules.items.properties.to.properties.prefix.enum.map(value => {
return (<MenuItem key={value} value={value}>{value}</MenuItem>)
})}
</Select>
</InputAdornment>
)
}}/>
</span>
{action === 'Set' ? (
<span className={classes.flexContainer}>
<Typography className={classes.rightJustifiedText} component='span' style={{width: '150px'}}>to</Typography>
<TextField variant={'outlined'}
label={'Value'}
className={classes.textField}
margin={textFieldMargin}
value={from.value}
fullWidth
disabled={!can}
error={!fromIsValid}
onChange={this.onChangeFromValue.bind(this, index)}
inputProps={{style: {paddingLeft: '4px'}}}
InputProps={{
style: {width: 'calc(100% - 14px)'},
startAdornment: (
<InputAdornment>
<Select value={from.type}
onChange={this.onChangeFromType.bind(this, index)}
IconComponent={CustomArrowIcon}
disabled={!can}
SelectDisplayProps={{
style: {paddingLeft: '24px', paddingRight: '0px'}
}}>
{schema.properties.rules.items.properties.from.properties.type.enum.map(value => {
return (<MenuItem key={value} value={value}>{value}</MenuItem>)
})}
</Select>
</InputAdornment>
)
}}/>
</span>
) : null}
</div>
</DraggableRow>)
})}
</Grid>
</React.Fragment>
)}
</Can>
</div>
);
}
}
Modify.defaultProps = {
requiredDocuments: {},
documents: {},
configValues: {
rules: []
},
serviceActionProfile: {}
};
Modify.propTypes = {
requiredDocuments: PropTypes.object,
documents: PropTypes.object,
configValues: PropTypes.object,
onChange: PropTypes.func.isRequired,
accountNumber: PropTypes.number.isRequired,
ability: PropTypes.object.isRequired,
serviceActionProfile: PropTypes.object
};
export default withStyles(styles)(Modify);

Redux initial value null while clicking on edit button

I am trying to edit form. After first rendering I am not getting existing data from database. Because of state is null. If I click on second button first data is appearing. ( Because of state is null. If I click on second button first data is appearing. )
Reducer:
const intialState ={ channel:null}
Please click here for the image
//form Component
import React, { Fragment } from "react";
import Button from "#material-ui/core/Button";
import TextField from "#material-ui/core/TextField";
import Dialog from "#material-ui/core/Dialog";
import DialogActions from "#material-ui/core/DialogActions";
import DialogContent from "#material-ui/core/DialogContent";
import DialogTitle from "#material-ui/core/DialogTitle";
const FormChannel = ({ channelData, ouSubmit, open }) => {
let [formData, setFromData] = React.useState({
channel: channelData ? channelData.channel : "",
channelName: channelData ? channelData.channelName : "",
introduction: channelData ? channelData.introduction : "",
language: channelData ? channelData.language : "",
keywords: channelData ? channelData.keywords : ""
});
const { channel, channelName, introduction, language, keywords } = formData;
const handleClose = () => {
ouSubmit(formData);
setFromData({
channel: "",
channelName: "",
introduction: "",
language: "",
keywords: ""
});
};
const handleChange = channel => e => {
setFromData({ ...formData, [channel]: e.target.value });
};
const view = (
<Fragment>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">Create Channel</DialogTitle>
<DialogContent>
<TextField
autoFocus
value={channel}
onChange={handleChange("channel")}
id="channel"
label="Name"
type="emachannelil"
margin="normal"
variant="outlined"
fullWidth
/>
<TextField
value={channelName}
onChange={handleChange("channelName")}
id="channelName"
label="Channel Name"
type="text"
margin="normal"
variant="outlined"
fullWidth
/>
<TextField
label="Language"
id="language"
value={language}
onChange={handleChange("language")}
type="text"
margin="normal"
variant="outlined"
fullWidth
/>
<TextField
value={introduction}
onChange={handleChange("introduction")}
id="introduction"
label="Introduction"
type="text"
margin="normal"
variant="outlined"
fullWidth
/>
<TextField
value={keywords}
onChange={handleChange("keywords")}
id="kewords"
label="Keywords"
type="text"
margin="normal"
variant="outlined"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary" variant="contained">
Create
</Button>
</DialogActions>
</Dialog>
</Fragment>
);
return <Fragment>{view}</Fragment>;
};
export default FormChannel
Edit
import React, { Fragment } from "react";
import { connect } from "react-redux";
import { Button } from "#material-ui/core";
import FormChannel from "./Form";
const EditChannel = ({ channels: { channel }, open, setOpen }) => {
console.log(channel);
return (
<Fragment>
<FormChannel
open={open}
channelData={channel}
ouSubmit={formData => {
setOpen(false);
console.log(formData);
}}
/>
</Fragment>
);
};
const mapStateToProps = (state, props) => {
console.log(state);
return {
channels: state.channels
};
};
export default connect(mapStateToProps)(EditChannel);
card
import React, { Fragment } from "react";
import { makeStyles } from "#material-ui/core/styles";
import { Typography, Button } from "#material-ui/core";
import Avatar from "#material-ui/core/Avatar";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { channelFollow, channelFetchById } from "../../../redux/action/channel";
import EditChannel from "../../Channels/List/Edit";
const useStyles = makeStyles(theme => ({
card: {
flexGrow: 1
},
img: {
height: "60%",
overflow: "hidden",
width: "100%"
},
link: {
textDecoration: "none",
color: "black"
},
link1: {
textDecoration: "none",
color: "white"
},
root: {
display: "flex",
"& > *": {
margin: theme.spacing(0.5)
},
justifyContent: "center"
},
button: {
display: "flex",
justifyContent: "center"
},
text: {
fontWeight: "bold",
fontSize: 15
},
text1: {
color: "gray",
fontSize: 15
},
bigAvatar: {
width: 140,
height: 140
},
buttons: {
marginRight: 5
}
}));
const ChannelCard = props => {
const classes = useStyles();
const { channel, channelFetchById } = props;
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const view = (
<div className={classes.card}>
<Link to={`/channels/${channel._id}`} className={classes.link}>
<div className={classes.root}>
<Avatar
alt="Remy Sharp"
src={channel.avatar}
className={classes.bigAvatar}
/>
</div>
<div className={classes.title}>
<Typography
variant="subtitle1"
align="center"
className={classes.text}
>
{channel.channelName}
</Typography>
<Typography variant="body2" align="center">
{channel.introduction}
</Typography>
</div>
</Link>
<Typography variant="body2" align="center" className={classes.text1}>
{channel.follows ? channel.follows.length : 0} followers <br />
Language:{channel.language}
</Typography>
<div className={classes.center}>
<div className={classes.button}>
<div className={classes.buttons}>
<Button
variant="contained"
color="primary"
onClick={() => {
channelFetchById(channel._id);
handleClickOpen();
}}
>
Edit
</Button>
{open ? <EditChannel setOpen={setOpen} open={open} /> : ""}
</div>
<div>
<Button color="primary" variant="contained">
Delete
</Button>
</div>
</div>
<br />
</div>
</div>
);
return <Fragment>{view}</Fragment>;
};
export default connect(null, { channelFollow, channelFetchById })(ChannelCard);
Reducer
import {
CHANNEL_FETCH,
CHANNEL_FETCH_BY_ID,
CHANNEL_UNFOLLOWED,
CHANNEL_EDIT
} from "../action/typeof";
const initialState = {
channels: [],
channel: null,
loading: true
};
export default (state = initialState, action) => {
const { payload } = action;
switch (action.type) {
case CHANNEL_FETCH:
return { ...state, channels: payload, loading: false };
case CHANNEL_FETCH_BY_ID:
return {
...state,
channel: payload,
loading: false
};
case CHANNEL_UNFOLLOWED:
return {
...state,
channelUnfollowedUser: payload,
loading: false
};
case CHANNEL_EDIT:
const channelEdit = state.channes.map(single =>
single._id === payload.id ? { single, ...payload.data } : single
);
return {
...state,
channels: channelEdit,
loading: false
};
default:
return state;
}
};

Material UI TextField Not Showing Value

I have a form in react where I'm using a Material UI TextField. I want a value present in the TextField and I can see that it is being set when I use the Inspect option of Google Chrome. In addition to that, I see that the type="hidden". I have changed this to type="text" and to no avail, the value is still not presented. Curious if anyone here has any understanding as to why this would happen. Below is the primary code that is causing the problem:
<TextField
name="Property"
select
fullWidth
margin="normal"
className={clsx(selectInputStyle.margin, selectInputStyle.textField, selectInputStyle.root)}
value={viewProperties[index].name}
onChange={this.handleSelectChange}
>
{propertyKeys.map((key, index) => (
<MenuItem value={key} key={index}>
{key}
</MenuItem>
))}
</TextField>
Here is the full code file just for a complete context of what is going on.
import React, { Component } from 'react';
import { Container, Form, Button, Row, Col, Nav, NavItem, NavLink, Input, FormGroup } from 'reactstrap';
import { connect } from 'react-redux';
import { reduxForm, FieldArray, arrayRemoveAll } from 'redux-form/immutable';
import * as Immutable from 'immutable';
import _ from 'lodash';
import { bindActionCreators } from 'redux';
import BlockUi from 'react-block-ui';
import MaterialButton from '#material-ui/core/Button';
import DeleteIcon from '#material-ui/icons/Delete';
import { makeStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
import MenuItem from '#material-ui/core/MenuItem';
import Select from '#material-ui/core/Select';
import InputMaterial from '#material-ui/core/Input';
import FormControl from '#material-ui/core/FormControl';
import TextField from '#material-ui/core/TextField';
import OutlinedInput from '#material-ui/core/OutlinedInput';
import clsx from 'clsx';
import { AvText, AvSelect } from '../forms/components';
import { showNotification, loadPayerProperties, updatePayerProperties } from '../actions';
class PayerPropertiesEditor extends Component {
constructor(props) {
super(props);
this.uploadRef = React.createRef();
this.state = {
errors: [],
refeshProperties: false,
blocking: false
};
this.showButton = false;
this.divPadding = { padding: '20px' };
this.doSubmit = this.doSubmit.bind(this);
this.handleInvalidSubmit = this.handleInvalidSubmit.bind(this);
this.renderProperties = this.renderProperties.bind(this);
this.handleSelectChange = this.handleSelectChange.bind(this);
this.useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1)
},
leftIcon: {
marginRight: theme.spacing(1)
},
rightIcon: {
marginLeft: theme.spacing(1)
},
iconSmall: {
fontSize: 20
},
root: {
display: 'flex',
flexWrap: 'wrap'
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
},
container: {
display: 'flex',
flexWrap: 'wrap'
},
input: {
margin: theme.spacing(1)
}
}));
}
componentDidMount() {
this.setState({ view: 'payer' });
}
componentDidUpdate(prevProps) {
const { loadPayerProperties } = this.props;
if (this.state.refeshProperties) {
this.props.arrayRemoveAll('payerPropertiesEditorForm', 'properties');
loadPayerProperties();
this.setState({ refeshProperties: false });
this.setState({ blocking: false });
this.showButton = false;
}
if (!prevProps.properties && this.props.properties) {
this.props.change('properties', Immutable.fromJS(this.props.properties));
}
}
doSubmit(values) {
const { updatePayerProperties } = this.props;
return new Promise(resolve => {
this.setState({
blocking: true
});
updatePayerProperties(values.toJS(), () => {
this.setState({ refeshProperties: true });
});
resolve();
});
}
handleInvalidSubmit() {
this.props.showNotification({
level: 'error',
message: 'Errors were found.'
});
}
handleSelectChange(event) {
console.log(event);
}
renderProperties({ fields }) {
const inputUseStyles = makeStyles(theme => ({
root: {
display: 'flex',
flexWrap: 'wrap'
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
},
container: {
display: 'flex',
flexWrap: 'wrap'
},
margin: {
margin: theme.spacing(1)
},
textField: {
flexBasis: 200
},
input: {
margin: theme.spacing(1)
}
}));
const selectInputStyle = inputUseStyles().input;
const useStyles = this.useStyles();
const { formProperties, unsetPropertiesKeys } = this.props;
const viewProperties = formProperties[`${this.state.view}`];
const propertyKeys = unsetPropertiesKeys[`${this.state.view}`];
return (
<div maxWidth="sm" className={selectInputStyle.root}>
{fields.map((property, index) => (
<Row
key={index}
style={{
display: viewProperties[index].action === 'remove' ? 'none' : ''
}}
>
<Col xs={5}>
<TextField
select
fullWidth
margin="normal"
className={clsx(selectInputStyle.margin, selectInputStyle.textField, selectInputStyle.root)}
value={viewProperties[index].name}
onChange={this.handleSelectChange}
>
{propertyKeys.map((key, index) => (
<MenuItem value={key} key={index}>
{key}
</MenuItem>
))}
</TextField>
</Col>
<Col xs={5}>
<AvText
name={`${property}.value`}
onChange={() => {
if (viewProperties[index].action !== 'add') {
this.props.change(`${property}.action`, 'update');
this.showButton = true;
}
}}
/>
</Col>
<Col xs={1}>
<MaterialButton
variant="contained"
className="{classes.button}"
onClick={() => {
fields.remove(index);
if (viewProperties[index].action !== 'add') {
fields.insert(
index,
Immutable.fromJS(Object.assign({}, viewProperties[index], { action: 'remove' }))
);
this.showButton = true;
}
}}
>
Delete
<DeleteIcon className={useStyles.rightIcon} />
</MaterialButton>
</Col>
</Row>
))}
<Row>
<Col xs={12}>
<Button
color="primary"
onClick={() => {
fields.push(
Immutable.fromJS({
owner: this.props.ownerKeys[this.state.view],
action: 'add'
})
);
this.showButton = true;
}}
>
Add Property
</Button>
</Col>
</Row>
<br />
{this.showButton === true && (
<Row>
<Col xs={12}>
<Button color="primary" type="submit">
Submit
</Button>
</Col>
</Row>
)}
</div>
);
}
render() {
const { handleSubmit, properties, payerName, chsId, parentChsId } = this.props;
const formStyles = makeStyles(theme => ({
root: {
display: 'flex',
flexWrap: 'wrap'
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
const navItems = ['payer', 'clearinghouse', 'parent'].map(key => (
<Tab
textColor="primary"
key={key}
label={
key === 'payer'
? 'PAYER: ' + payerName
: key === 'clearinghouse'
? 'CLEARING HOUSE: ' + chsId
: 'PARENT: ' + parentChsId
}
onClick={() => this.setState({ view: key })}
/>
));
const overrides = this.state.view === 'payer' ? ['clearinghouse'] : this.state.view === 'parent' ? ['parent'] : [];
const readonly = properties
? overrides
.filter(key => key !== this.state.view)
.map(key => properties[key])
.reduce((acc, val) => acc.concat(val), [])
.map((property, idx) => {
return (
<Row key={idx}>
<Col xs={5}>
<FormGroup>
<Input value={property.name} disabled />
</FormGroup>
</Col>
<Col xs={5}>
<FormGroup>
<Input value={property.value} disabled />
</FormGroup>
</Col>
</Row>
);
})
: [];
return (
<BlockUi tag="div" blocking={this.state.blocking} className="my-2">
<Container maxWidth="sm">
<AppBar position="static" color="default">
<Tabs variant="fullWidth" textColor="primary">
{navItems}
</Tabs>
</AppBar>
<FormControl
fullWidth
className="mt-4"
onSubmit={handleSubmit(this.doSubmit)}
ref={form => (this.formRef = form)}
>
{readonly}
<FieldArray
name={`properties.${this.state.view}`}
component={this.renderProperties}
rerenderOnEveryChange
/>
</FormControl>
</Container>
</BlockUi>
);
}
}
const mapStateToProps = state => {
const {
payerPropertiesStore: {
payer: { payerId, payerName, chsId, parentChsId },
properties,
propertyKeys
},
form: {
payerPropertiesEditorForm: {
values: { properties: formProperties }
}
}
} = state.toJS();
const unsetPropertiesKeys = {};
for (const view of ['payer', 'clearinghouse', 'parent']) {
unsetPropertiesKeys[view] = propertyKeys.filter(key => !_.find(formProperties[view], { name: key }));
}
const ownerKeys = { payer: payerId, clearinghouse: chsId, parent: parentChsId };
return { formProperties, properties, ownerKeys, unsetPropertiesKeys, payerId, payerName, chsId, parentChsId };
};
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
showNotification,
loadPayerProperties,
updatePayerProperties,
arrayRemoveAll
},
dispatch
);
export default reduxForm({
form: 'payerPropertiesEditorForm',
enableReinitialize: true,
initialValues: Immutable.fromJS({ properties: {} })
})(
connect(
mapStateToProps,
mapDispatchToProps
)(PayerPropertiesEditor)
);
In the TextField you are setting value from viewProperties like,
value={viewProperties[index].name}
You are getting data in viewProperties from formProperties based on this.state.view
const viewProperties = formProperties[`${this.state.view}`];
As you are setting view state in componentDidMount, on intial render view is not set and don't have value so you are not getting any value from formProperties.
You need to have a default state,
this.state = {
errors: [],
refeshProperties: false,
blocking: false,
view: 'payer' // default set
};

Is there is way to set pagination arrows to be enabled even there is no data?

how can i set pagination arrows to be enabled even there is no data or even if i'll use a condition to switch disable/enablePagination Arrows
import React from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
import { NavLink } from 'react-router-dom';
import Truncate from 'react-truncate';
// Material-UI
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import AddIcon from '#material-ui/icons/Add';
import EditIcon from '#material-ui/icons/Edit';
import MaterialTable, { MTableToolbar, TablePagination } from 'material-table';
import IconButton from '#material-ui/core/IconButton';
import Tooltip from '#material-ui/core/Tooltip';
import Typography from '#material-ui/core/Typography';
import Zoom from '#material-ui/core/Zoom';
// Components
import Entity from '~/Components/Entity';
import violationsStyles from './styles';
import Strings from '~/Services/Strings';
// Services
import Navigate from '~/Services/Navigate';
#withStyles(violationsStyles)
class Violations extends React.Component {
state = {
data : [],
pageIndex: 0,
pageSize: 1,
totalCount: 0
}
componentDidMount() {
this.get();
}
get() {
const { pageIndex, pageSize } = this.state;
this.entity.get({ pageIndex, pageSize });
}
get options() {
return {
actionsColumnIndex: -1,
pageSize: 10,
filtering: true,
columnsButton: true,
maxBodyHeight: 550,
doubleHorizontalScroll: true,
headerStyle: {
color: '#434343',
fontSize: 13
}
};
}
get localization() {
return {
header: {
actions: '',
},
body: {
emptyDataSourceMessage: Strings.listEmptyLabel,
},
pagination: {
labelRowsPerPage: Strings.rowsPerPageLabel,
labelDisplayedRows: `{from}-{to} ${Strings.fromText} {count}`,
},
toolbar: {
addRemoveColumns: Strings.addRemoveColumns,
showColumnsTitle: Strings.showColumnsTitle
},
};
}
get columns() {
const { classes } = this.props;
return [
{ title: Strings.violationReferenceNumber, field: 'referenceNumber', cellStyle: { width: 110 } },
{
title: Strings.violationDescription,
field: 'description',
render: rowData => (
<Typography>
<Truncate lines={1} ellipsis={<span>... </span>}>
{rowData.description}
</Truncate>
</Typography>
),
cellStyle: { paddingLeft: 0 }
},
{ title: Strings.violationPenalty,
field: 'penaltyTypeId',
lookup: {
1: Strings.inform,
2: Strings.alert,
3: Strings.suspension,
},
cellStyle: { width: '120px' }
},
{
title: Strings.violationStatus,
field: 'isArchived',
lookup: {
false: Strings.violationIsNotSettled,
true: Strings.violationIsSettled,
},
cellStyle: { width: '130px' },
defaultFilter: [ 'false' ]
},
{
title: Strings.listActionsLabel,
field: 'isArchived',
render: rowData => (
<div className={classes.iconWrapper}>
<Choose>
<When condition={rowData.isArchived === 'true'}>
<Tooltip TransitionComponent={Zoom} title={Strings.violationEditActionOn}>
<span>
<IconButton disabled={rowData.isArchived === 'true'}>
<EditIcon fontSize="default"/>
</IconButton>
</span>
</Tooltip>
</When>
<Otherwise>
<IconButton disabled={rowData.isArchived === 'true' ? true : false} onClick={() => Navigate.go(`/violations/editor/${rowData.id}`)}>
<Tooltip TransitionComponent={Zoom} title={Strings.violationListEditLabel}>
<EditIcon fontSize="default"/>
</Tooltip>
</IconButton>
</Otherwise>
</Choose>
</div>
),
filtering: false,
cellStyle: { paddingLeft: 35, paddingRight: 75, textAlign: 'left', justifyContent: 'flex-end', display: 'flex' },
headerStyle: { paddingLeft: 35, textAlign: 'left', }
},
];
}
get components() {
const { classes } = this.props;
return {
Toolbar: props => (
<div className={classes.toolbar}>
<MTableToolbar {...props} />
<div className={classes.customActionsBar}>
<Button component={NavLink} to={'/violations/editor'} variant={'outlined'} color={'primary'}>
<AddIcon className={classes.rightIcon} />
{Strings.addNewViolation}
</Button>
</div>
<div className={classes.tabelSecondHeader}>
<Typography variant='h6'>
{Strings.listOfViolations}
</Typography>
</div>
</div>
),
Pagination: props => (
<TablePagination {...props}
count={this.state.totalCount}
/>
),
};
}
onEntityReceived(data) {
const arr = data.result;
const mutableList = [];
if(arr && arr.length > 0) {
arr.map(item => {
mutableList.push({
...item,
isArchived: item.isArchived.toString()
});
});
this.setState({
data: mutableList,
totalCount: data.totalCount
});
}
}
render() {
const { data } = this.state;
return (
<React.Fragment>
<Helmet>
<title>
{Strings.violationsManegment}
</title>
</Helmet>
<Entity
storeId={'Supervision-Violations'}
entityRef={ref => { this.entity = ref; }}
onEntityPosted={() => this.onEntityPosted()}
onEntityReceived={data => this.onEntityReceived(data)}
render={store => (
<MaterialTable
title={Strings.violationsManegment}
data={data}
isLoading={store.loading}
options={this.options}
localization={this.localization}
columns={this.columns}
components={this.components}
onChangePage={pageIndex => this.setState({ pageIndex })}
onChangeRowsPerPage={pageSize => this.setState({ pageSize })}
/>
)}
/>
</React.Fragment>
);
}
}
Violations.propTypes = {
classes: PropTypes.object,
};
export default Violations;
i need to update the count of table because i'm getting the data from back-end and i'm using server side pagination technique and it appears that the total counts automatically be the total rows received unless i mutated it to be be total count that i received from endpoint because i receive some data per page
Use nextIconButtonProps={{disabled:false}} and backIconButtonProps={{disabled:false}}
could you try using the override that is explained here https://material-table.com/#/docs/features/component-overriding.
Use the code above to try to find a way of that you want. Right now there isnt a property that you can do that in a easy way.
components={{
Pagination: props => (
<TablePagination
{...props}
rowsPerPageOptions={[5, 10, 20, 30]}
rowsPerPage={this.state.numberRowPerPage}
count={this.state.totalRow}
page={
firstLoad
? this.state.pageNumber
: this.state.pageNumber - 1
}
onChangePage={(e, page) =>
this.handleChangePage(page + 1)
}
onChangeRowsPerPage={event => {
props.onChangeRowsPerPage(event);
this.handleChangeRowPerPage(event.target.value);
}}
/>
),
}}

Resources