Is refactoring with a separate function possible? - javascript

I have a table component where many of the table columns are handled similarly.
Is there any way to optimize this code, maybe in a separate function?
import useTableStyles from 'admin/components/table/AdminTable.styles';
import useStyles from 'portal/pages/wasOperators/views/ViewEditOperators.style';
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { toast } from 'react-toastify';
import { Form } from 'shared/components/Form';
import WssoService from 'shared/services/WssoService';
import DateFnsUtils from '#date-io/date-fns';
import { Button, Grid, TextField } from '#material-ui/core';
import { DatePicker, MuiPickersUtilsProvider } from '#material-ui/pickers';
interface Props {
isEditing?: boolean;
}
const BenchmarkingTable: React.FC<Props> = ({ isEditing }) => {
const tableClasses = useTableStyles();
const classes = useStyles();
const { t } = useTranslation();
const { operatorsId }: any = useParams();
const valuesForInverseCalculation = [
'continuityWaterSupply',
'totalLossesWaterSupplySystems',
'pressureWaterSupplySystem',
'sewerNetworkAccidents',
'floodsThirdPartyCausedBySewage',
];
const benchmarkingDetailsInit = {
levelWaterSupplyServices: {
target: '',
result: '',
status: '',
},
qualityDrinkWaterLargeAreas: {
target: '',
result: '',
status: '',
},
qualityDrinkWaterSmallAreas: {
target: '',
result: '',
status: '',
},
monitorQualityDrinkWater: {
target: '',
result: '',
status: '',
},
continuityWaterSupply: {
target: '',
result: '',
status: '',
},
totalLossesWaterSupplySystems: {
target: '',
result: '',
status: '',
},
pressureWaterSupplySystem: {
target: '',
result: '',
status: '',
},
levelCoverageServiceDisposalOfWastewater: {
target: '',
result: '',
status: '',
},
levelCoverageServiceTreatmentOfWastewater: {
target: '',
result: '',
status: '',
},
wastewaterQuality: {
target: '',
result: '',
status: '',
},
sewerNetworkAccidents: {
target: '',
result: '',
status: '',
},
floodsThirdPartyCausedBySewage: {
target: '',
result: '',
status: '',
},
};
const [data, setData] = useState<any>(benchmarkingDetailsInit);
const [KEY, setKEY] = useState<any>(new Date());
useEffect(() => {
(async () => {
const result: any = await WssoService.getBenchmarking(
operatorsId,
KEY.getFullYear()
);
if (result && result.data && result.data.json) {
setData(JSON.parse(result.data.json));
} else {
setData(null);
}
})();
/* eslint-disable react-hooks/exhaustive-deps */
}, [KEY, operatorsId]);
const HandleKEYChange = () => {
return (
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<DatePicker
variant="inline"
inputVariant="outlined"
format={'yyyy'}
views={['year']}
onChange={setKEY}
value={KEY}
/>
</MuiPickersUtilsProvider>
);
};
const updateBenchmarking = async (editValues: any) => {
const request = {
json: JSON.stringify(editValues),
};
const result: any = await WssoService.editAddBenchmarking(
operatorsId,
KEY.getFullYear(),
request
);
if (result && result.data && result.json) {
setData(JSON.parse(result.data.json));
}
};
const onUpdateSuccess = () => {
toast.success(t('itemUpdateSuccessfully'));
};
const handleSaveData = async () => {
if (data) {
await updateBenchmarking(data);
onUpdateSuccess();
}
};
const handleOnTextChange = (
key: string,
valueKey: string,
e: React.ChangeEvent<HTMLInputElement>
) => {
const value = e.target.value;
const newData = Object.assign({}, data, {
[key]: Object.assign({}, data[key], {
[valueKey]: value,
}),
});
setData(newData);
};
const handleColorChange = (name: any, target: any, result: any) => {
const removePercentFromTarget = target.includes('%')
? target.slice(0, -1)
: target;
const convertedTarget = Number(removePercentFromTarget);
const removePercentFromResult = result.includes('%')
? result.slice(0, -1)
: result;
const convertedResult = Number(removePercentFromResult);
target = convertedTarget;
result = convertedResult;
let final: any;
const arrOfNames = [];
arrOfNames.push(name);
if (arrOfNames.some(x => valuesForInverseCalculation.includes(x))) {
if (target < result) {
Object.keys(data).map(k => {
return (final = data[k].status = 'red');
});
} else if (target > result) {
Object.keys(data).map(k => {
return (final = data[k].status = 'green');
});
} else if (target === result) {
Object.keys(data).map(k => {
return (final = data[k].status = 'yellow');
});
}
} else {
if (target > result) {
Object.keys(data).map(k => {
return (final = data[k].status = 'red');
});
} else if (target < result || (target === result && result === 100)) {
Object.keys(data).map(k => {
return (final = data[k].status = 'green');
});
} else if (target === result) {
Object.keys(data).map(k => {
return (final = data[k].status = 'yellow');
});
}
}
return final;
};
const updateData = (newData: any[]) => {
setData(newData);
};
const renderBenchmarkDetails = () => {
if (isEditing) {
return data ? (
Object.keys(data).map(k => {
return (
<tr>
<td>{t(k)}</td>
<td className={classes.benchTextfieldAlign}>
<TextField
type="text"
onChange={(e: any) => handleOnTextChange(k, 'target', e)}
value={data[k].target}
inputProps={{ min: 0, style: { textAlign: 'center' } }}
required
/>
</td>
<td className={classes.benchTextfieldAlign}>
<TextField
type="text"
onChange={(e: any) => handleOnTextChange(k, 'result', e)}
value={data[k].result}
inputProps={{ min: 0, style: { textAlign: 'center' } }}
required
/>
</td>
</tr>
);
})
) : (
<tr>
<td>{t('noDetailsToDisplay')}</td>
<td>{t('noDetailsToDisplay')}</td>
<td>{t('noDetailsToDisplay')}</td>
<td>{t('noDetailsToDisplay')}</td>
</tr>
);
} else {
return data ? (
Object.keys(data).map((rowName, i) => {
return (
<tr key={i}>
<td>{t(rowName)}</td>
<td className={classes.benchTextfieldAlign}>
{data[rowName].target}
</td>
<td className={classes.benchTextfieldAlign}>
{data[rowName].result}
</td>
<td className={classes.benchTextfieldAlign}>
<div
style={{
width: '15px',
height: '15px',
borderRadius: '50%',
backgroundColor: handleColorChange(
rowName,
data[rowName].target,
data[rowName].result
),
}}
/>
</td>
</tr>
);
})
) : (
<tr>
<td>{t('noDetailsToDisplay')}</td>
<td>{t('noDetailsToDisplay')}</td>
<td>{t('noDetailsToDisplay')}</td>
<td>{t('noDetailsToDisplay')}</td>
</tr>
);
}
};
const handleExport = useCallback(async () => {
const dataRows: any = [];
if (data) {
Object.keys(data).map(rowName => {
dataRows.push([t(rowName), data[rowName].target, data[rowName].result]);
return null;
});
}
let dataToCSV: any[][] = [];
if (dataRows && dataRows.length > 0) {
dataToCSV = [[t('criteria'), t('targets'), t('results')], ...dataRows];
const csvContent: string =
'data:text/csv;charset=utf-8,\uFEFF' +
dataToCSV
.map(e =>
e
.map(r =>
r instanceof Array
? `"${(r ?? '').toString().replace(',', ', ')}"`
: (r ?? '').toString().replace(',', ' ')
)
.join(',')
)
.join('\n');
const encodedUri: string = encodeURI(csvContent);
const link: HTMLAnchorElement = document.createElement('a');
link.setAttribute('href', encodedUri);
link.setAttribute('download', `${t('exportedTableData')}.csv`);
link.click();
}
}, [data, t]);
console.log(operatorsId);
console.log(benchmarkingDetailsInit);
console.log(updateBenchmarking);
return (
<Grid>
<Form
onSubmit={(formData, { resetForm }) => {
const tempData = !!operatorsId
? {
...formData,
}
: {
...formData,
...(!!operatorsId && {
operatorsAreaEntity: {
id: parseInt(operatorsId, 0),
},
}),
id: operatorsId ? undefined : Date.now(),
};
updateData([tempData, ...data]);
resetForm();
}}
initialValues={benchmarkingDetailsInit}
enableReinitialize={true}
>
<div className={classes.tableContainer}>
<Grid item xs={12}>
<div className={classes.containerLegendBench}>
<div
style={{
display: 'flex',
justifyContent: 'flex-end',
}}
>
<div className={classes.yearBoxBenchmarketType}>
<HandleKEYChange />
</div>
<div className={classes.legendBoxBenchmarketType}>
{t('reachedResult')}
<div
style={{
marginLeft: '3px',
width: '11px',
height: '9px',
borderRadius: '50%',
backgroundColor: '#006400',
display: 'inline-block',
}}
/>
</div>
<div className={classes.legendBoxBenchmarketType}>
{t('almostReachedResult')}
<div
style={{
marginLeft: '3px',
width: '11px',
height: '9px',
borderRadius: '50%',
backgroundColor: '#FFDF00',
display: 'inline-block',
}}
/>
</div>
<div className={classes.legendBoxBenchmarketType}>
{t('notReachedResult')}
<tr
style={{
marginLeft: '3px',
width: '11px',
height: '9px',
borderRadius: '50%',
backgroundColor: '#FF0000',
display: 'inline-block',
}}
/>
</div>
</div>
</div>
</Grid>
<table className={tableClasses.table}>
<thead>
<tr>
<th>{t('criteria')}</th>
<th>{t('targets')}</th>
<th>{t('results')}</th>
<th />
</tr>
</thead>
<tbody>{renderBenchmarkDetails()}</tbody>
</table>
</div>
<Grid item xs={12}>
<Button
style={{ margin: '5px 0px 10px 10px' }}
type="button"
color="primary"
variant="contained"
onClick={handleExport}
disabled={!data}
>
{t('export')}
</Button>
</Grid>
{isEditing && (
<Button onClick={handleSaveData} className={classes.submitButton}>
{t('save')} <i className={`fas fa-save ${classes.submitIcon}`} />
</Button>
)}
</Form>
</Grid>
);
};
export default BenchmarkingTable;

You've got 60 lines of code just to initialize an empty object! It seems like each key of this object is a row of your table and each value shares the same format:
interface Benchmark {
target: string;
result: string;
status: string;
}
So the data can be described as Record<string, Benchmark>. Though it might make more sense to make the name be a property and let data be an array.
Your condition with the <td>{t('noDetailsToDisplay')}</td> will never be hit because you've initialized data to your empty object so data is always true.
If you want to show something different when there is not data then you should do any one of these:
Use an undefined initial state.
Have a separate isLoading state that you set with your useEffect hooks.
Check Object.keys(data).length > 0 instead of checking !! data.
Another part of your code that seems majorly illogical is the handleColorChange function. Despite the misleading name, it seems like this function is designed to compute a color string based on the result and target values. This color is the status property and you mutate state to get it there. Instead you should compute the color and add it as status after you fetch the data and before you set it to state.
If you want a row with 4 empty cells, you can do this:
<tr>
{Array.from({ length: 4 }).map((_, i) => (
<td key={i}>{t("noDetailsToDisplay")}</td>
))}
</tr>
You have that same JSX block in two places so you want to fix that.
I haven't fixed everything, but I have fixed quite a lot.
import useTableStyles from 'admin/components/table/AdminTable.styles';
import useStyles from 'portal/pages/wasOperators/views/ViewEditOperators.style';
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { toast } from 'react-toastify';
import { Form } from 'shared/components/Form';
import WssoService from 'shared/services/WssoService';
import DateFnsUtils from '#date-io/date-fns';
import { Button, Grid, TextField } from '#material-ui/core';
import { DatePicker, MuiPickersUtilsProvider } from '#material-ui/pickers';
interface BenchmarkFromService {
target: string;
result: string;
}
interface BenchmarkRow extends BenchmarkFromService {
name: string;
status: string;
}
const BenchmarkingTable: React.FC<Props> = ({ isEditing }) => {
const tableClasses = useTableStyles();
const classes = useStyles();
const { t } = useTranslation();
const { operatorsId } = useParams<{ operatorsId: string }>();
/**
* each row of the table is a Benchmark
*/
const [data, setData] = useState<BenchmarkRow[]>([]);
const [KEY, setKEY] = useState(new Date());
// extract shared logic from "get" and "update" responses
// memoized so it's safe as a useEffect dependency
const handleResult = useCallback(
// is this really any? what is it when it's not { data: { json: string } }
(result: any) => {
/**
* regular colors and inverted colors have the same logic, just swap the target and result
*/
const computeColor = (a: number, b: number): string => {
if (a > b) {
return "red";
} else if (a < b || (a === b && b === 100)) {
return "green";
} else if (a === b) {
return "yellow";
}
};
const valuesForInverseCalculation = [
"continuityWaterSupply",
"totalLossesWaterSupplySystems",
"pressureWaterSupplySystem",
"sewerNetworkAccidents",
"floodsThirdPartyCausedBySewage"
];
const toNum = (value: string): number =>
parseFloat(value.replace("%", ""));
try {
const raw: Record<string, BenchmarkFromService> = JSON.parse(
result.data.json
);
const formatted = Object.entries(raw).map(
([name, { target, result }]) => ({
name,
target,
result,
status: valuesForInverseCalculation.includes(name)
? computeColor(toNum(result), toNum(target))
: computeColor(toNum(target), toNum(result))
})
);
setData(formatted);
} catch (e) {
// catch errors from malformatted response or unparsable JSON
setData([]);
}
},
[]
);
useEffect(() => {
(async () => {
const result: any = await WssoService.getBenchmarking(
operatorsId,
KEY.getFullYear()
);
handleResult(result);
})();
}, [KEY, operatorsId, handleResult]);
const updateBenchmarking = async (editValues: any) => {
const request = {
json: JSON.stringify(editValues)
};
const result: any = await WssoService.editAddBenchmarking(
operatorsId,
KEY.getFullYear(),
request
);
handleResult(result);
};
const onUpdateSuccess = () => {
toast.success(t("itemUpdateSuccessfully"));
};
const handleSaveData = async () => {
if (data) {
await updateBenchmarking(data);
onUpdateSuccess();
}
};
const renderCell = (property: keyof BenchmarkRow, row: BenchmarkRow) => {
const onChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
setData((previous) =>
previous.map((obj) =>
obj.name === row.name
? {
...obj,
[property]: e.target.value
}
: obj
)
);
};
return (
<td className={classes.benchTextfieldAlign}>
{isEditing ? (
<TextField
type="text"
onChange={onChange}
value={row[property]}
inputProps={{ min: 0, style: { textAlign: "center" } }}
required
/>
) : (
row[property]
)}
</td>
);
};
const renderBenchmarkDetails = () => {
if (data.length === 0) {
return (
<tr>
{Array.from({ length: 4 }).map((_, i) => (
<td key={i}>{t("noDetailsToDisplay")}</td>
))}
</tr>
);
}
return data.map((row) => (
<tr key={row.name}>
<th scope="row">{row.name}</th>
{renderCell("target", row)}
{renderCell("result", row)}
<td className={classes.benchTextfieldAlign}>
<div
style={{
width: "15px",
height: "15px",
borderRadius: "50%",
backgroundColor: row.status
}}
/>
</td>
</tr>
));
};
const handleExport = useCallback(async () => {
if (data.length === 0) {
return;
}
const dataRows = data.map((row) => [t(row.name), row.target, row.result]);
const dataToCSV = [
[t("criteria"), t("targets"), t("results")],
...dataRows
];
const csvContent: string =
"data:text/csv;charset=utf-8,\uFEFF" +
dataToCSV
.map((row) =>
row
.map(
(cell) => cell.replace(",", "\\,") // I think it's ok to escape the comma like this?
)
.join(",")
)
.join("\n");
const encodedUri: string = encodeURI(csvContent);
const link: HTMLAnchorElement = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", `${t("exportedTableData")}.csv`);
link.click();
}, [data, t]);
return (
<Grid>
<Form
onSubmit={(formData, { resetForm }) => {
const tempData = !!operatorsId
? {
...formData
}
: {
...formData,
...(!!operatorsId && {
operatorsAreaEntity: {
id: parseInt(operatorsId, 0)
}
}),
id: operatorsId ? undefined : Date.now()
};
setData([tempData, ...data]);
resetForm();
}}
initialValues={{}}
enableReinitialize={true}
>
<div className={classes.tableContainer}>
<Grid item xs={12}>
<div className={classes.containerLegendBench}>
<div
style={{
display: "flex",
justifyContent: "flex-end"
}}
>
<div className={classes.yearBoxBenchmarketType}>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<DatePicker
variant="inline"
inputVariant="outlined"
format={"yyyy"}
views={["year"]}
onChange={setKEY}
value={KEY}
/>
</MuiPickersUtilsProvider>
</div>
<div className={classes.legendBoxBenchmarketType}>
{t("reachedResult")}
<div
style={{
marginLeft: "3px",
width: "11px",
height: "9px",
borderRadius: "50%",
backgroundColor: "#006400",
display: "inline-block"
}}
/>
</div>
<div className={classes.legendBoxBenchmarketType}>
{t("almostReachedResult")}
<div
style={{
marginLeft: "3px",
width: "11px",
height: "9px",
borderRadius: "50%",
backgroundColor: "#FFDF00",
display: "inline-block"
}}
/>
</div>
<div className={classes.legendBoxBenchmarketType}>
{t("notReachedResult")}
<tr
style={{
marginLeft: "3px",
width: "11px",
height: "9px",
borderRadius: "50%",
backgroundColor: "#FF0000",
display: "inline-block"
}}
/>
</div>
</div>
</div>
</Grid>
<table className={tableClasses.table}>
<thead>
<tr>
<th>{t("criteria")}</th>
<th>{t("targets")}</th>
<th>{t("results")}</th>
<th />
</tr>
</thead>
<tbody>{renderBenchmarkDetails()}</tbody>
</table>
</div>
<Grid item xs={12}>
<Button
style={{ margin: "5px 0px 10px 10px" }}
type="button"
color="primary"
variant="contained"
onClick={handleExport}
disabled={!data}
>
{t("export")}
</Button>
</Grid>
{isEditing && (
<Button onClick={handleSaveData} className={classes.submitButton}>
{t("save")} <i className={`fas fa-save ${classes.submitIcon}`} />
</Button>
)}
</Form>
</Grid>
);
};
export default BenchmarkingTable;

Related

What am I doing wrong? when my season dropdown (AsyncSelect) renders my seasons, it shows season 1 twice? not season 1 then season 2?

`What am I doing wrong? when my season dropdown (AsyncSelect) renders my seasons, it shows season 1 twice? not season 1 then season 2?
Also in the same modal I want to run the episode items via Flatlist, but initially season 1 then 2 and so on in asyncSelect, if yal know of a better dropdown or current picker for reactjs.
import { ReactNode, SetStateAction, useEffect, useRef, useState } from "react";
import { useRecoilState, useRecoilValue } from "recoil";
import { CheckIcon, HandThumbUpIcon, PlusIcon, SpeakerWaveIcon, SpeakerXMarkIcon, XMarkIcon } from "#heroicons/react/24/outline";
import MuiModal from "#mui/material/Modal";
import toast, { LoaderIcon, Toaster } from "react-hot-toast";
import { FaPlay } from "react-icons/fa";
import videojs from "videojs";
import VideoPlayer from "./../src/VideoPlayer";
import FlatList from "flatlist-react";
import Dots from "react-activity/dist/Dots";
import "react-activity/dist/Dots.css";
import Dropdown from 'react-dropdown';
import 'react-dropdown/style.css';
import Select, { ActionMeta, StylesConfig, Theme } from 'react-select';
import AsyncSelect, { useAsync } from 'react-select/async';
import useAuth from "../hooks/useAuth";
import { episodeState, genreState, modalState, seasonsState, vodAssetState } from "../atoms/modalAtom";
//import { Element } from "../typings";
import { VodAsset, Genre, VodSeason, Episode, VideoObject } from "../src/models";
import { View } from "#aws-amplify/ui-react";
import awsvideoconfig from "../src/aws-video-exports";
import { Storage } from "aws-amplify";
import EpisodeItem from "./EpisodeItem";
import { DropdownItem } from "reactstrap";
import VodAssetItem from "./VodAssetItem";
import { DropdownIndicator } from "react-select/dist/declarations/src/components/indicators";
interface ModalProps {
episode: Episode;
episodes: Episode[];
vodAsset: VodAsset;
//video: VideoObject;
seasons: VodSeason[];
season: VodSeason;
genres: Genre[];
genre: Genre;
}
function Modal (this: any, { vodAsset, episode, episodes, seasons, season, genres, genre }: ModalProps): JSX.Element {
const [showModal, setShowModal] = useRecoilState(modalState);
const [items, setItems] = useState([]);
const [selectedItem, setSelectedItem] = useState(null);
const [selectedItems, setSelectedItems] = useState([]);
const [inputValue, setInputValue] = useState('');
const [selectedValue, setSelectedValue] = useState(null);
const [itemValue, setItemValue] = useState('');
const [itemIndex, setItemIndex] = useState('');
const [currentVodAsset, setCurrentVodAsset] = useRecoilState(vodAssetState);
const [currentEpisode, setCurrentEpisode] = useRecoilState(episodeState);
const [currentSeason, setCurrentSeason] = useRecoilState(seasonsState);
const [currentGenre, setCurrentGenre] = useRecoilState(genreState);
//const [vodAssetName, setVodAssetName] = useState(vodAsset?.title);
//const [vodAssetDescription, setVodAssetDescription] = useState(vodAsset?.description);
//const [vodAssetGenre, setVodAssetGenre] = useState(vodAsset?.genreID); //genre?.name);
//const [vodAssetRating, setVodAssetRating] = useState(vodAsset?.popularity);
//const [vodAssetYear, setVodAssetYear] = useState(vodAsset?.release_date);
//const [vodAssetDuration, setVodAssetDuration] = useState(vodAsset?.duration); //duration is a number
//const [vodAssetImage, setVodAssetImage] = useState(vodAsset?.poster);
const [vodAssetVideo, setVodAssetVideo] = useState(vodAsset?.video);
const [vodAssetSeasons, setVodAssetSeasons] = useState(vodAsset?.seasons); //seasons is an array
//const [vodAssetEpisodes, setVodAssetEpisodes] = useState(vodAsset?.episode); //episode is a number
const [vodAssetSeasonsList, setVodAssetSeasonsList] = useState(vodAsset?.seasons); //seasons is an array
//const [vodAssetEpisodesList, setVodAssetEpisodesList] = useState(vodAsset?.episodes); //episode is a number
const seasonNames = seasons ? seasons.map(season => season?.name) : []; //season?.name);
const episodeNames = episodes ? episodes.map(episode => episode?.title) : [];
const [seasonsList, setSeasonsList] = useState(seasonNames);
const [episodesList, setEpisodesList] = useState(episodeNames);
const firstSeason = vodAsset?.seasons?[0]:season!; //season! is a non-null assertion operator, double check this
const firstEpisode = firstSeason?[0]:episode!; //episode! is a non-null assertion operator, double check this
const [selectedSeason, setSelectedSeason] = useState(null); //useState<VodSeason | null>(firstSeason);//(season?.name);
const [selectedEpisode, setSelectedEpisode] = useState(null); //useState<Episode | null>(firstEpisode);//(episode?.title);
const [seasonName, setSeasonName] = useState(seasonNames[0]); //season?.name);
const [selectedSeasonName, setSelectedSeasonName] = useState(seasonNames[0]); //useState<VodSeason | null>(firstSeason);//(season?.name);
const [selectedEpisodeName, setSelectedEpisodeName] = useState(episodeNames[0]); //useState<Episode | null>(firstEpisode);//(episode?.title);
const [muted, setMuted] = useState(false);
const [addedToList, setAddedToList] = useState(false);
const [videoURL, setVideoURL] = useState('');
const [status, setStatus] = useState({});
const [token, setToken] = useState('');
//const [video, setVideo] = useState<VideoObject>();
//const onChangeEpisode = (e: any) => {
//const episodeName = e.value;} //e.value is the selected episode name from the dropdown menu
const toastStyle = {
background: 'black',
color: '#d4af37',
fontWeight: 'bold',
fontSize: '16px',
padding: '15px',
borderRadius: '9999px',
maxWidth: '1000px',
};
//const onChangeSeason = (s: VodSeason) => {
// const seasonNames = seasons ? seasons.map(season => season?.name) : []};
//const seasonName = s.name;} //e.value is the selected season name from the dropdown menu
const [selectedOption, setSelectedOption] = useState(null); //useState<VodSeason | null>(firstSeason);//(season?.name);
const [togglePlaceholderClassName, setTogglePlaceholderClassName] = useState('hidden');
const [toggleMyOptionsClassName, setToggleMyOptionsClassName] = useState('hidden');
const [toggleClassName, setToggleClassName] = useState('hidden');
const [toggleMenuClassName, setToggleMenuClassName] = useState('hidden');
const options = [
{ id: season?.id, value: season?.name, name: season?.name, data: season?.name, label: 'Season 1'},
{ id: season?.id, value: season?.name, name: season?.name, data: season?.name, label: 'Season 2'},
];
{/*const customStyles = {
control: (base: any, state: any) => ({
...base,
background: 'black',
color: 'white',
// match with the menu
borderRadius: state.isFocused ? '3px 3px 0 0' : 3,
// Overwrittes the different states of border
borderColor: state.isFocused ? '#d4af37' : '#d4af37',
// Removes weird border around container
boxShadow: state.isFocused ? null : null,
'&:hover': {
// Overwrittes the different states of border
borderColor: state.isFocused ? '#d4af37' : '#d4af37',
},
}),
menu: (base: any) => ({
...base,
// override border radius to match the box
borderRadius: 0,
// kill the gap
marginTop: 0,
background: 'black',
color: 'white',
}),
menuList: (base: any) => ({
...base,
// kill the white space on first and last option
padding: 0,
}),
option: (base: any, state: any) => ({
...base,
background: 'black',
color: 'white',
// padding: '20px',
// color: state.isSelected ? 'red' : 'blue',
// backgroundColor: state.isSelected ? 'yellow' : 'green',
// the rest of your styles
}),
};*/}
const colorStyles = {
control: (styles: any) => ({ ...styles, backgroundColor: 'black' }),
option: (styles: any, { data, isDisabled, isFocused, isSelected }: any) => {
console.log('option', data, isDisabled, isFocused, isSelected);
return {
...styles,
//color: data.color,
backgroundColor: isDisabled
? null
: isSelected
? '#d4af37'
: isFocused
? '#d4af37'
: null,
color: isDisabled
? '#ccc'
: isSelected
? 'black'
: isFocused
? 'black'
: 'white',
cursor: isDisabled ? 'not-allowed' : 'default',
};
}
};
// handle selection
const handleChange = (selectedOption: any) => {
console.log('handleChange', selectedOption);
setSelectedValue(selectedOption);
};
{/*const handleChange = value => {
console.log('handleChange', value);
setSelectedValue(value);
};*/}
// handle input change
//const handleInputChange = (value: SetStateAction<string>) => {
//console.log('handleInputChange', value);
//setInputValue(value);
//};
const loadOptions = (searchValue: string, callback: (arg0: { id: string, data: string, name: string, value: string; label: string; }[]) => void) => {
setTimeout(() => {
const filteredOptions = options.filter(option =>
option.label.toLowerCase().includes(searchValue.toLowerCase())
);
console.log('loadOptions', searchValue, filteredOptions);
callback(filteredOptions);
}, 1000);
};
{/*const { data, error, isLoading } = useAsync({ promiseFn: loadOptions });*/}
const handleClose = () => {
setShowModal(false)
}
const defaultOption = options[0];
const VIDEOJS_OPTIONS = {
autoplay: "play",
controls: true,
responsive: true,
fluid: true,
preload: "auto",
width: 1080,
//muted: {muted},
onplaying,
sources: {
src: awsvideoconfig.awsOutputIVS,
},
useNativeControls: true,
html5: {
nativeAudioTracks: false,
nativeVideoTracks: false,
}
};
const videoOnDemandJsOptions = {
//ref: {vodAsset},
autoplay: "play",
controls: true,
responsive: true,
fluid: true,
preload: "auto",
width: 1080,
token: {token},
displayingVodAsset: true,
//VideoPlaybackQuality: "HIGH",
//muted: {muted},
onplaying,
sources: {
//ref: {vodAsset},
src: { uri: videoURL}, //`https://${awsvideoconfig.awsOutputVideo}/public/RPS-Trailer-2/RPS-Trailer-2.m3u8`, // Test Link: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" / or / uri: videoURL / {uri: episode?.video?.uri},
posterSource: {uri: episode?.poster},
data: {episodes},
//renderEpisodeItem: {EpisodeItem},
episode: currentEpisode,
chosenItem: episode,
posterStyle: {
resizeMode: 'cover',
},
type: "application/x-mpegURL" //"video/mp4"
},
usePoster: true,
useNativeControls: true,
resizeMode: "contain",
//onPlaybackStatusUpdate: {status: setStatus(() => status)},
//episode: currentEpisode,
//data: {episodes},
//renderEpisodeItem: {EpisodeItem}, // this is the component that renders the episode item
//chosenItem: item,
//token: item.video.token,
html5: {
nativeAudioTracks: false,
nativeVideoTracks: false,
}
};
useEffect(() => {
async function fetchVodAsset() {
const result = (await DataStore.query(VodAsset, {id: vodAsset.id})) // double check this
//setVodAsset(result);
console.log("VOD ASSET:", result)
}
fetchVodAsset();
}, [])
//console.log("VOD ASSET:", vodAsset);
useEffect(() => {
if (!vodAsset) {
return;
}
async function fetchSeasons() {
const vodAssetSeasons = (await DataStore.query(VodSeason)).filter(s => s.VodAsset?.id === vodAsset.id)
//setSeasons(seasons);
console.log("SEASONS:", vodAssetSeasons);
setCurrentSeason(vodAssetSeasons[0]);
}
fetchSeasons();
}, [vodAsset])
useEffect(() => {
if (!currentSeason) {
return;
}
async function fetchEpisodes() {
const seasonEpisode = (await DataStore.query(Episode)).filter(e => e.vodSeason?.id === currentSeason.id);
//setEpisodes(seasonEpisode);
console.log("EPISODES:", seasonEpisode);
setCurrentEpisode(seasonEpisode[0])
}
fetchEpisodes();
}, [currentSeason])
if (!vodAsset) {
return <Dots/>
}
console.log("season:", season);
console.log("seasonNames:", seasonNames);
{/*useEffect(() => {
if (episode?.video.startsWith('http')) {
setVideoURL(episode?.video);
return;
}
Storage.get(episode?.video, {level: 'public', contentType: 'application/x-mpegURL', region: 'us-east-1'}).then(setVideoURL);
}, [episode]);*/}
{/*useEffect(() => {
if (!video) {
return;
}
(async () => {
await video?.current?.unloadAsync();
await video?.current?.loadAsync(
{ uri: videoURL },
{},
false
);
})();
}, [videoURL]);*/}
//console.log(videoURL);
//if (videoURL === '') {
//return <View />;
//}
//OR
//useEffect(() => {
//if (!vodAsset) return
//async function fetchVodAsset() {
//const data = await fetch(
//`https://api.themoviedb.org/3/${
//{/*vodAsset?.media_type === 'tv' ? 'tv' : 'movie'*/}
//}/${vodAsset?.id}?api_key=${
//process.env.NEXT_PUBLIC_API_KEY
//}&language=en-US&append_to_response=videos`
//).then((response) => response.json())
//if (data?.videos) {
//const index = data.videos.results.findIndex(
//(element: Element) => element.type === 'Trailer'
//)
//setTrailer(data.videos?.results[index]?.key)
//}
//if (data?.genres) {
//setGenres(data.genres)
//}
//}
//fetchVodAsset()
//}, [vodAsset])
// Find all the movies in the user's list
{/*useEffect(() => {
if (user) {
return onSnapshot(
collection(db, 'customers', user.uid, 'myList'),
(snapshot) => setVodAssets(snapshot.docs)
)
}
}, [db, vodAsset?.id])*/}
// Check if the movie is already in the user's list
{/*useEffect(
() =>
setAddedToList(
vodAssets.findIndex((result) => result.data().id === vodAsset?.id) !== -1
),
[vodAssets]
)*/}
const handleList = async () => {/*
if (addedToList) {
await deleteDoc(
doc(db, 'customers', user!.uid, 'myList', vodAsset?.id.toString()!)
)
toast(
`${vodAsset?.title || vodAsset?.original_name} has been removed from My List`,
{
duration: 8000,
style: toastStyle,
}
)
} else {
await setDoc(
doc(db, 'customers', user!.uid, 'myList', vodAsset?.id.toString()!),
{ ...vodAsset }
)
toast(
`${vodAsset?.title || vodAsset?.original_name} has been added to My List`,
{
duration: 8000,
style: toastStyle,
}
)
}
*/}
return (
<MuiModal
id="modal"
open={showModal}
onClose={handleClose}
className="fixed !top-4 left-0 right-0 z-50 mx-auto w-full max-w-5xl overflow-hidden
overflow-y-scroll rounded-md scrollbar-hide"
>
<>
<Toaster position="bottom-center" />
<button
onClick={handleClose}
className="modalButton absolute right-5 top-5 !z-40 h-9 w-9 border-none
bg-[#181818] hover:bg-[#181818]"
>
<XMarkIcon className="h-6 w-6"/>
</button>
<div className="relative pt-[0%]" key={vodAsset?.id}>
<VideoPlayer episode={currentEpisode} {...videoOnDemandJsOptions} />
<FlatList
list={[]}
data={episodes}
renderItem={function (item: Episode, {/*key: string*/}): JSX.Element | ReactNode {
<EpisodeItem
key={episode.id}
episode={item}
/>;
throw new Error("Function not implemented.");
}}>
</FlatList>
{/*<EpisodeItem episode={episode} />*/}
<div className="absolute bottom-10 flex w-full items-center justify-between px-10">
<div className="flex space-x-2">
<button className="flex items-center gap-x-2 rounded bg-[#d4af37] px-8 text-xl
font-bold text-black transition hover:bg-[#e6e6e6]"
>
<FaPlay className="h-7 w-7 text-black" />
Play
</button>
<button className="modalButton" onClick={handleList}>
{addedToList ? (
<CheckIcon className="h-7 w-7" />
): (
<PlusIcon className="h-7 w-7" />
)}
</button>
<button className="modalButton">
<HandThumbUpIcon className="h-7 w-7" />
</button>
</div>
<button className="modalButton" onClick={() => setMuted(!muted)}>
{muted ? (
<SpeakerXMarkIcon className="h-6 w-6" />
) : (
<SpeakerWaveIcon className="h-6 w-6" />
)}
</button>
</div>
</div>
<div className="flex space-x-16 rounded-b-md bg-[#181818] px-10 py-8">
<div className="space-y-6 text-lg">
<div className="flex items-center space-x-2 text-sm">
<p className="font-semibold text-green-400">
{/*vodAsset!.vote_average * 10*/}98% Match
</p>
<p className="font-light">
{vodAsset?.release_date || vodAsset?.first_air_date}
</p>
<p className="font-light">
{vodAsset?.numberOfSeasons} Seasons
</p>
<div className="flex h-4 items-center justify-center rounded border
border-white/40 px-1.5 bg-[#d4af37] text-black text-xs">
12+
</div>
<div className="flex h-4 items-center justify-center rounded border
border-white/40 px-1.5 text-xs">
HD
</div>
</div>
{/*<View style={{ backgroundColor: 'white' }}>
</View>*/}
{/*currentSeason && (
<Picker
selectedValue={currentSeason}
onValueChange={(itemValue, itemIndex) => {
setCurrentSeason(seasons[itemIndex])
}}
style={{color: 'white', width: 150 }}
itemStyle={{backgroundColor: 'black', maxHeight: 50, marginBottom: -150, marginTop: 20 }}
dropdownIconColor={'white'}
>
{seasonNames?.map(seasonName => (
<Picker color='#d4af37' label={seasonName} value={seasonName} key={seasonName} />
))}
</Picker>
)*/}
{currentSeason &&
<>
{seasonNames?.map(seasonName => (
<option id={seasonName} label={seasonName} value={seasonName} key={season.id}>{seasonName}</option>
))}
<AsyncSelect value={seasons?.map(season => ({value: season.name, label: season.name}))} onChange={handleChange} //value={seasons?.map(season => ({ id: season?.id, name: season.name, value: season.name, label: season.name}))} onChange={handleChange} //value={seasonName}
//value={seasons?.map(season => ({value: season.name, label: season.name}))}
className="select"
classNamePrefix="select"
autoFocus={true}
defaultValue={options[0]}
//defaultValue={seasons?.map(season => ({value: season.name, label: season.name}))}
isSearchable={false}
name="season"
cacheOptions={false}
loadOptions={loadOptions}
defaultOptions={true}
options={options}
placeholder="Select a season"
styles={colorStyles}
//onChange={onChangeSeason}
//onChange={(itemValue, itemIndex) => {
//setCurrentSeason(seasons[itemIndex])
//}}
//controlShouldRenderValue={true}
//isClearable={true}
isMulti={true}
//isOptionDisabled={true}
//isOptionSelected={true}
//isSearchable={true}
//isDisabled={true}
//isFocused={true}
//isLoading={true}
//isRtl={true}
//isInvalid={true}
//menuIsOpen={true}
//isMenuFocused={true}
//isMenuSelected={true}
/>
</>}
<div className="flex flex-col gap-x-10 gap-y-4 font-light md:flex-row">
<p className="w-5/6">{/*vodAsset?.overview*/}{vodAsset?.description}
</p>
<div className="flex flex-col space-y-3 text-sm">
<div>
<span className="text-[gray]">Cast: </span>
{vodAsset?.cast}
</div>
<div>
<span className="text-[gray]">Creator: </span>
{vodAsset?.creator}
</div>
<div>
<span className="text-[gray]">Genres: </span>
{/*genres?.map((genre: { title: any; }) => genre?.title)*/}
{/*vodAsset?.genreID*/}
</div>
<div>
<span className="text-[gray]">Original Language: </span>
{vodAsset?.original_language}
</div>
<div>
<span className="text-[gray]">Original Country: </span>
{vodAsset?.origin_country}
</div>
<div>
<span className="text-[gray]">Total Votes: </span>
{vodAsset?.vote_count}</div>
</div>
</div>
</div>
</div>
</>
</MuiModal>
)
}
export default Modal`````

State is updating but not rendering the UI when clicking button

I have 3 inputs and I have a state where I store all the inputs in an object. I have a button that clears all the states of the 3 inputs. When I'm logging the console it's actually clearing the inputs but it's not updating the UI. Do I have to get the previous state and clear it and not just set the new object to empty values?
Here is the code:
/*eslint-disable*/
import React, { useState, useMemo } from 'react';
import TextField from '#mui/material/TextField';
import Button from '#mui/material/Button';
import { SortProps, SorterProps } from '../../types/Sorter';
import SorterField from './SorterField';
import styles from '../../assets/components/Sorter/sorter.module.scss';
import '../../assets/utils/_sorterContainer.scss';
const initSelects = (fields) => {
return Object.fromEntries(
fields.map(({ name, defaultValue }) => [name, defaultValue ?? ''])
);
};
const Sorter = ({ menuItemsValue, setSortData, activeSort }: SortProps) => {
const fields: SorterProps[] = [
{
name: 'sorterParam1',
title: 'Sort by',
optional: true,
options: [],
},
{
name: 'sorterParam2',
title: 'Then by',
optional: true,
options: [],
},
{
name: 'sorterParam3',
title: 'Then by',
optional: true,
options: [],
},
];
const [select, setSelect] = useState<any>(() => initSelects(fields));
const fieldsWithOptions = useMemo(() => {
const taken = new Set();
return fields.map((field) => {
// options without previous selections
const withOptions = {
...field,
options: menuItemsValue.filter((item) => !taken.has(item)),
};
// only track non-empty values
if (select[field.name]) {
// exclude the current field selection
taken.add(select[field.name]);
}
return withOptions;
});
}, [fields, menuItemsValue, select]);
const NewIcon = (props) => (
<svg
{...props}
className="sorter-dropdown"
width="8"
height="5"
viewBox="0 0 8 5"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M1 1L4 3.5L7 1" stroke="#4C4C4D" stroke-linecap="round" />
</svg>
);
const handleSelectChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
const { name, value } = e.target;
setSelect({ ...select, [name]: value });
};
const handleClearAllInputs = () => {
setSelect({
sorterParam1: '',
sorterParam2: null,
sorterParam3: null,
});
};
const handleConfirm = () => {
setSortData(
select.sorterParam3 || select.sorterParam2 || select.sorterParam1
);
};
return (
<TextField
label="Advanced Sorting"
className={styles.sorter__inputs}
id="sorter-parameter-1"
variant="standard"
InputProps={{
disableUnderline: true,
}}
select
SelectProps={{
IconComponent: (props) => <NewIcon {...props} />,
}}
sx={{
fontSize: '12px',
width: '100%',
'& .MuiInputBase-input:focus': {
backgroundColor: 'transparent !important',
},
'& .MuiInputLabel-root': {
color: '#9E9E9E',
},
'& .MuiTextField-root': {
fontSize: '13px',
},
'& .MuiOutlinedInput-root': {
backgroundColor: '#fff',
},
}}
>
{fieldsWithOptions.map((option, index) => {
return (
<SorterField
key={option.name}
menuItemsValue={option.options}
name={option.name}
option={option}
count={fields.length}
handleChange={handleSelectChange}
index={index + 1} // setData={setData}
activeSort={activeSort}
setSortData={setSortData}
/>
);
})}
<div className={styles.sorter__inputControllers}>
<Button
className={styles.sorter__clearAllInput}
onClick={handleClearAllInputs}
>
Clear All
</Button>
<Button
onClick={() => handleConfirm()}
className={styles.sorter__confirmInput}
>
Confirm
</Button>
</div>
</TextField>
);
};
export default Sorter;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

setState not updating value with object

This is my initial state:
const [inputList, setInputList] = useState([
{ answer: "Answer 1", isCorrect: false, points: 0 },
]);
I am adding input field dynamically and initially there will be one field. There is an Add button through which I can add more fields and remove button with that I can remove fields. But the problem I'm facing, after submitting the value I want to clear all the fields and there should be only one field as it was initially.Suppose if I add 4 fields and after submitting there should be only one. But when I'm trying to do that its not working but add, remove and even setting empty the state is working but setting only one field is not working.I'm mapping depending on inputList value but still after submitting I'm getting all the fields. If I have added 4 fields then after submitting also its 4. The state value is not being changed at all.
ADD more function:
const handleAddClick = () => {
let count = inputList.length;
let newfield = {
answer: `Answer ${count + 1}`,
isCorrect: false,
points: 0,
};
setInputList([...inputList, newfield]);
};
remove function:
const handleRemoveClick = (index) => {
let list = [...inputList];
let newList = list.filter((item, ind) => ind !== index);
setInputList(newList);
};
submit function: This should reset the state to initial state that is only one field should be there But not working
const Save = () => {
setInputList([{ answer: "Answer 1", isCorrect: false, points: 0 }])
}
I tried to do loop also for deleting every index but it only removes one no matter how many times the loop runs
Whole Component:
import DeleteIcon from "#mui/icons-material/Delete";
import React from "react";
import Checkbox from "#mui/material/Checkbox";
import Radio from "#mui/material/Radio";
import FormControl from "#mui/material/FormControl";
import IconButton from "#mui/material/IconButton";
import Switch from "#mui/material/Switch";
import TextField from "#mui/material/TextField";
import { useState } from "react";
import PlusSign from "../../../../Files/img/plus.png";
import Header from "../../../Header/Header";
import Topbar from "../../../Topbar/Topbar";
import styles from "../MultipleResponse/MultipleResponse.module.scss";
import QuestionEditor from "../../Editor/QuestionEditor/QuestionEditor";
import ReactTag from "../../TagInput/ReactTag";
import FormControlLabel from "#mui/material/FormControlLabel";
import { styled } from "#mui/material/styles";
import axios from "axios";
import "react-quill/dist/quill.snow.css";
import AnswerEditor from "../../Editor/AnswerEditor/AnswerEditor";
import SelectComp from "../../SelectComp/SelectComp";
import {
timeOptions,
categoryOptions,
difficultyLevelOptions,
} from "../../../../utils/constants";
import { RadioGroup } from "#mui/material";
import Snackbars from "../../../Assesment/MyAssesment/Snackbar";
import { useEffect } from "react";
export default function MultipleChoice() {
const baseURL = "http://localhost:5000/api/question";
const [distributePoints, setDistributePoints] = useState(false);
const [inputList, setInputList] = useState([
{ answer: "Answer 1", isCorrect: false, points: 0 },
]);
const [question, setQuestion] = useState("");
const [tags, setTag] = useState([]);
const [title, setTitle] = useState("");
const [time, setTime] = useState("");
const [difficultyLevel, setdifficultyLevel] = useState("");
const [category, setCategory] = useState("");
const [whatToLook, setwhatToLook] = useState("");
const [questionRelevent, setQuestionRelevent] = useState("");
const [shuffle, setShuffle] = useState(false);
const [value, setValue] = React.useState("");
const [loading, setLoading] = useState(false);
const [loading2, setLoading2] = useState(false);
const [snackText, setSnackText] = useState("");
const [severity,setSeverity]=useState("")
useEffect(() => {
const items = JSON.parse(localStorage.getItem("tempData"));
if (items) {
setTitle(items[0].title);
setdifficultyLevel(items[0].level);
setwhatToLook(items[0].whatToLookFor);
setQuestionRelevent(items[0].whyQuestionIsRelevant);
setTag(items[0].tags);
setTime(items[0].time);
setInputList(items[0].answers);
setCategory(items[0].category);
setQuestion(items[0].detail);
setShuffle(items[0].shuffleAnswers);
setDistributePoints(items[0].distributePoints);
setValue(items[0].radioValue);
}
}, []);
const [snack, setSnack] = React.useState(false);
const closeSnackbar = (event, reason) => {
if (reason === "clickaway") {
return;
}
setSnack(false);
};
const IOSSwitch = styled((props) => (
<Switch
focusVisibleClassName=".Mui-focusVisible"
disableRipple
{...props}
/>
))(({ theme }) => ({
width: 40,
height: 24,
padding: 0,
marginRight: 10,
"& .MuiSwitch-switchBase": {
padding: 0,
margin: 2,
transitionDuration: "300ms",
"&.Mui-checked": {
transform: "translateX(16px)",
color: "#fff",
"& + .MuiSwitch-track": {
backgroundColor:
theme.palette.mode === "dark" ? "#3699FF" : "#3699FF",
opacity: 1,
border: 0,
},
"&.Mui-disabled + .MuiSwitch-track": {
opacity: 0.5,
},
},
"&.Mui-focusVisible .MuiSwitch-thumb": {
color: "#33cf4d",
border: "6px solid #fff",
},
"&.Mui-disabled .MuiSwitch-thumb": {
color:
theme.palette.mode === "light"
? theme.palette.grey[100]
: theme.palette.grey[600],
},
"&.Mui-disabled + .MuiSwitch-track": {
opacity: theme.palette.mode === "light" ? 0.7 : 0.3,
},
},
"& .MuiSwitch-thumb": {
boxSizing: "border-box",
width: 20,
height: 20,
backgroundColor: "#FFFFFF",
},
"& .MuiSwitch-track": {
borderRadius: 26 / 2,
backgroundColor: theme.palette.mode === "light" ? "#E9E9EA" : "#39393D",
opacity: 1,
transition: theme.transitions.create(["background-color"], {
duration: 500,
}),
},
}));
const switchHandler = (event) => {
setDistributePoints(event.target.checked);
};
const handleInputChange = (e, index) => {
const list = [...inputList];
list[index]["answer"] = e;
setInputList(list);
};
const handlePointChange = (e, index) => {
const { name, value } = e.target;
const list = [...inputList];
list[index][name] = value;
setInputList(list);
};
const handleRemoveClick = (index) => {
let list = [...inputList];
let newList = list.filter((item, ind) => ind !== index);
setInputList(newList);
};
const handleAddClick = () => {
let count = inputList.length;
let newfield = {
answer: `Answer ${count + 1}`,
isCorrect: false,
points: 0,
};
setInputList([...inputList, newfield]);
};
const SaveAndAddAnother = () => {
setLoading2(true);
const newInputList = inputList.map(({ points, ...rest }) => {
return rest;
});
try {
axios
.post(baseURL, {
questionType: "MULTIPLE_CHOICE",
time: time.toString(),
title: title,
level: difficultyLevel,
detail: question,
category: category,
tags: tags,
whatToLookFor: whatToLook,
whyQuestionIsRelevant: questionRelevent,
answers: distributePoints ? inputList : newInputList,
distributePoints: distributePoints,
shuffleAnswers: shuffle,
})
.then((response) => {
setLoading(false);
if (response.data.result === 1) {
setSnack(true);
setSeverity('success')
setTimeout(() => {
setLoading2(false);
}, 1000);
setSnackText("Question added successfully");
setTime("");
setTitle("");
setdifficultyLevel("");
setQuestion("");
setCategory("");
setTag([]);
setwhatToLook("");
setQuestionRelevent("");
setDistributePoints(false);
setShuffle(false);
setValue("A");
let newList = [{ answer: "Answer 1", isCorrect: false, points: 0 }];
setInputList(newList);
localStorage.removeItem("tempData");
} else {
setLoading(false);
}
})
.catch((err) => {
// Handle error
setLoading2(false);
setSnack(true)
setSnackText("Error Adding question")
setSeverity('error')
});
} catch (e) {
setLoading(false);
}
};
const Save = () => {
setInputList([{ answer: "Answer 1", isCorrect: false, points: 0 }])
// setSnack(true);
// setSnackText("Question Saved as draft");
// setSeverity('success')
// setLoading(true);
// const localData = [
// {
// questionType: "MULTIPLE_CHOICE",
// time: time.toString(),
// title: title,
// level: difficultyLevel,
// detail: question,
// category: category,
// tags: tags,
// whatToLookFor: whatToLook,
// whyQuestionIsRelevant: questionRelevent,
// answers: inputList,
// distributePoints: distributePoints,
// shuffleAnswers: shuffle,
// radioValue: value,
// },
// ];
// localStorage.setItem("tempData", JSON.stringify(localData));
// setTimeout(() => {
// setLoading(false);
// }, 2000);
};
const questionText = (value) => {
setQuestion(value);
};
const selectedTags = (tags) => {
setTag(tags);
};
const handleTitle = (e) => {
setTitle(e.target.value);
};
const handleTime = (e) => {
setTime(e.target.value);
};
const handleDifficulty = (e) => {
setdifficultyLevel(e.target.value);
};
const handleCategory = (e) => {
setCategory(e.target.value);
};
const handleWhatToLookFor = (e) => {
setwhatToLook(e.target.value);
};
const handleQuestionRelevent = (e) => {
setQuestionRelevent(e.target.value);
};
const handleShuffle = (event) => {
setShuffle(event.target.checked);
};
function handleRadioButton(event, i) {
if (event.target.value === value) {
setValue("");
} else {
setValue(event.target.value);
}
const list = [...inputList];
Object.keys(list).forEach((key) => {
list[key]["isCorrect"] = false;
});
list[i]["isCorrect"] = true;
setInputList(list);
}
return (
<div className={styles.mainContainer}>
<Header />
<Topbar
questionType="Multiple-response"
loading={loading}
loading2={loading2}
SaveAndAddAnother={SaveAndAddAnother}
save={Save}
/>
<div className={styles.Container}>
<div className={styles.questionContainer}>
<div className={styles.left}>
<p className={styles.question}>Question</p>
<div className={styles.input}>
<TextField
variant="standard"
InputProps={{
disableUnderline: true,
}}
className={styles.title}
placeholder="Title.."
value={title}
onChange={(e) => handleTitle(e)}
/>
<QuestionEditor
question={question}
questionText={questionText}
theme="snow"
/>
</div>
<div className={styles.category}>
<ReactTag tags={tags} selectedTags={selectedTags} />
</div>
</div>
<div className={styles.right}>
<div className={styles.rightText}>
<span className={styles.heading}>Select the right answer</span>
<div>
<IOSSwitch
checked={distributePoints}
onChange={switchHandler}
/>
<span className={styles.instructinHeading}>
Distribute points across answers
</span>
</div>
</div>
<div className={styles.answers}>
<div className={styles.radio}>
<FormControl style={{ display: "flex", flex: 1 }}>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value}
>
{inputList.map((x, i) => {
return (
<div key={i}>
<div key={i} className={styles.options}>
{!distributePoints ? (
<FormControlLabel
key={i}
value={x.answer}
control={
<Radio
onClick={(e) => handleRadioButton(e, i)}
/>
}
defaultChecked={false}
/>
) : (
""
)}
<div className="editor">
<AnswerEditor
handleAnswer={handleInputChange}
index={i}
theme="bubble"
val={x.answer}
/>
</div>
{distributePoints ? (
<div className={styles.inputCounter}>
<TextField
variant="standard"
name="points"
InputProps={{
disableUnderline: true,
type: "number",
}}
inputProps={{
min: 0,
style: { textAlign: "center" },
}}
value={x.points}
onChange={(e) => handlePointChange(e, i)}
/>
</div>
) : (
""
)}
{inputList.length > 1 && (
<IconButton
onClick={() => handleRemoveClick(i)}
className={styles.icon}
>
<DeleteIcon
sx={{ fontSize: 24, color: "#3699FF" }}
/>
</IconButton>
)}
</div>
{inputList.length - 1 === i && (
<div className={styles.bottomItemContainer}>
{distributePoints ? (
<div className={styles.pointsInfoText}>
Allocate points across answers. Give the best
answer 5 points
</div>
) : (
""
)}
{!inputList.some((el) => el.isCorrect === true) &&
!distributePoints ? (
<div className={styles.pointsInfoText}>
Select the correct answer
</div>
) : (
""
)}
<div
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<button
onClick={handleAddClick}
type="button"
className={styles.addButton}
>
<img src={PlusSign} alt="" /> Add another
answer
</button>
<div className={styles.label}>
<Checkbox
checked={shuffle}
onChange={handleShuffle}
style={{ color: "#00A3FF" }}
/>
Shuffle answer
</div>
</div>
</div>
)}
</div>
);
})}
</RadioGroup>
</FormControl>
</div>
</div>
</div>
</div>
<div className={styles.itemContainer}>
<div className={styles.timeContainer}>
<SelectComp
handleChange={handleTime}
title="Time to answer the question"
val={time}
optionData={timeOptions}
/>
</div>
<div className={styles.timeContainer}>
<SelectComp
handleChange={handleCategory}
title="Question Category"
val={category}
optionData={categoryOptions}
/>
</div>
</div>
<div className={styles.itemContainer}>
<div style={{ flex: 1 }}>What to look for in the answer?</div>
<div style={{ flex: 1 }}>Why is this question relevant?</div>
</div>
<div className={styles.itemContainer}>
<div className={styles.releventText}>
<TextField
variant="standard"
InputProps={{
disableUnderline: true,
}}
className={styles.text}
placeholder="What to look for in the answer..."
value={whatToLook}
onChange={(e) => handleWhatToLookFor(e)}
/>
</div>
<div className={styles.releventText}>
<TextField
variant="standard"
InputProps={{
disableUnderline: true,
}}
className={styles.text}
placeholder="Why is this question relevant.."
value={questionRelevent}
onChange={(e) => handleQuestionRelevent(e)}
/>
</div>
</div>
<div className={styles.itemContainer}>
<div className={styles.difficultyLevel}>
<SelectComp
handleChange={handleDifficulty}
title="Difficulty Level"
val={difficultyLevel}
optionData={difficultyLevelOptions}
/>
</div>
{/* <div style={{ flex: 1 }}>Why is this question relevant?</div> */}
</div>
<div className={styles.bottomInfo}>
<div className={styles.element}>
<div className={styles.circle}></div>
<div className={styles.text}>
How should I formate my questions?
</div>
</div>
<div className={styles.element}>
<div className={styles.circle2}></div>
<div className={styles.text}>
{" "}
How do I use the question editor?
</div>
</div>
<div className={styles.element}>
<div className={styles.circle3}></div>
<div className={styles.text}>How do I use the formula editor?</div>
</div>
</div>
</div>
{snack && (
<Snackbars
text={snackText}
snack={snack}
closeSnackbar={closeSnackbar}
severity={severity}
/>
)}
{/* <div style={{ marginTop: 20 }}>{JSON.stringify(inputList)}</div> */}
</div>
);
}

Enter text in input of sub component then updates father component state are always interrupted in React?

Writing a react today meets a very strange problem. I get the data in the parent component and then pass it to the child component. There are four types of child components, but the third one (as shown below) is input in the input. The time is always interrupted, but the other three can run normally, so the problem is where the source code is attached below, the simulation data has been written in the code, any help will be greatly appreciated.
the problem image url is:https://assets.asifx.com/QQ20190130-141357.gif
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { message, Icon, Button, Table, Modal, Input, Select, Drawer, Form, Col, Row, DatePicker, } from 'antd';
class ListGroup extends Component {
constructor(props) {
super(props);
this.state = {
addText: '',
};
}
static getDerivedStateFromProps(props, state) {
}
onAdd = () => {
const { onChange } = this.props;
let { data } = this.props;
const { addText } = this.state;
if (typeof onChange === 'function') {
if (!Array.isArray(data)) {
data = [];
}
data.push(addText);
onChange(data);
}
}
onChange = (e) => {
const { data, onChange } = this.props;
if (data[e.target.name] && typeof onChange === 'function') {
data[e.target.name] = e.target.value;
onChange(data);
}
}
onDelete = (index) => {
const { data, onChange } = this.props;
if (data[index] && typeof onChange === 'function') {
data.splice(index, 1);
onChange(data);
}
}
render() {
const { data } = this.props;
const { addText } = this.state;
return (
<div>
<Input.Group compact style={{ marginBottom: '24px' }}>
<Input key='addinput' style={{ width: '84%' }} value={addText} onChange={e => this.setState({ addText: e.target.value })} />
<Button key='addbutton' style={{ width: '16%' }} type='primary' onClick={this.onAdd}>增加</Button>
</Input.Group>
{
(Array.isArray(data) && data || []).map((item, i) => <Input.Group compact>
<Input key={i} name={i} style={{ width: '84%' }} value={item} onChange={this.onChange} />
<Button style={{ width: '16%' }} type='danger' onClick={e => this.onDelete(i)}>删除</Button>
</Input.Group>)
}
</div>
);
}
}
class Dict extends Component {
constructor(props) {
super(props);
this.state = {
addText1: '',
addText2: '',
};
}
static getDerivedStateFromProps(props, state) {
}
onAdd = () => {
const { data, onChange } = this.props;
const { addText1, addText2 } = this.state;
if (typeof onChange === 'function') {
if (!Array.isArray(data)) {
data = [];
}
data.unshift([addText1, addText2]);
onChange(data);
this.setState({ addText1: '', addText2: '' });
}
}
onDelete = (index) => {
const { data, onChange } = this.props;
if (data[index] && typeof onChange === 'function') {
data.splice(index, 1);
onChange(data);
}
}
onKeyChange = (index, value) => {
const { data, onChange } = this.props;
if (data[index] && typeof onChange === 'function') {
data[index][0] = value;
onChange(data);
}
}
onValueChange = (index, value) => {
const { data, onChange } = this.props;
if (data[index] && typeof onChange === 'function') {
data[index][1] = value;
onChange(data);
}
}
render() {
const { data } = this.props;
const { addText1, addText2 } = this.state;
return (
<div>
<Input.Group key='add' compact style={{ marginBottom: '24px' }}>
<Input key='addinput1' style={{ width: '24%' }} value={addText1} onChange={e => this.setState({ addText1: e.target.value })} />
<Input key='addinput2' style={{ width: '60%' }} value={addText2} onChange={e => this.setState({ addText2: e.target.value })} />
<Button key='addbutton' style={{ width: '16%' }} type='primary' onClick={this.onAdd}>增加</Button>
</Input.Group>
{
(Array.isArray(data) && data || []).map((item, i) =>
<Input.Group key={i} compact>
<Input style={{ width: '24%' }} key={`key${item}`} name={`key${item}`} value={item[0]} onChange={e => this.onKeyChange(i, e.target.value)} />
<Input style={{ width: '60%' }} key={`val${item}`} name={`val${item}`} value={item[1]} onChange={e => this.onValueChange(i, e.target.value)} />
<Button style={{ width: '16%' }} type='danger' onClick={e => this.onDelete(i)}>删除</Button>
</Input.Group>)
}
</div>
);
}
}
class Home extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
visible: false,
confirmLoading: false,
roleOptions: [],
searchText: '',
data: [],
current: 1,
pageSize: 10,
total: 0,
info: {},
columns: [
{
title: '序号',
dataIndex: 'key',
key: 'key'
},
{
title: '类型',
dataIndex: 'type',
key: 'type'
},
{
title: '描述',
dataIndex: 'desc',
key: 'desc'
},
{
title: '变量',
dataIndex: 'variable',
key: 'variable'
},
{
title: '操作',
key: 'operation',
render: item =>
<span>
<a
href='javascript:;'
onClick={e => { this.setState({ visible: true, info: item }) }}
>
编辑
</a>
|
<a
href='javascript:;'
onClick={e => this.onDelete(item.id, item.isActive)}
>
删除
</a>
</span>
}
],
}
this.onSearch = this.onSearch.bind(this);
this.handleOk = this.handleOk.bind(this);
}
componentDidMount() {
const { current, pageSize, searchText } = this.state;
this.getData(current, pageSize, searchText);
}
getDataFromProps = (objData) => {
let data = [];
(Object.keys(objData) || []).forEach(item => {
data.push([item, objData[item]]);
});
return data;
}
async getData(current = this.state.current, pageSize = this.state.pageSize, searchText = this.state.searchText) {
this.setState({ isLoading: true });
let data = [], total = 0;
try {
// const resp = await fetch(`http://192.168.1.164:10086/bot/api/query_config?index=${current}&page_size=${pageSize}&query=${searchText}`, {
// method: 'GET',
// headers: {
// // 'Content-Type': 'application/json',
// },
// mode: 'cors',
// });
// const body = await resp.json();
const body = {
data: {
'data': [
{
'id': 1,
'desc': '用户介绍',
'variable': 'PERSON_INTRO_LIST',
'value': [
'进群须知 1. 最多邀请琥珀进5个群 2. 群人数需要在7-100之间,否则无法激活. 琥珀暂不支持私聊,进群后才能互动',
'安达斯',
'阿山东济南'
],
'type': 'list'
},
{
'id': 2,
'desc': '机器人名称',
'variable': 'BOT_NAME',
'value': '琥珀',
'type': 'text'
},
{
'id': 18,
'desc': '欢迎语',
'variable': 'GREET_MAPPING_DICT',
'value': {
'早上': 'morning',
'早晨好': 'morning',
'早安': 'morning',
'上午好': 'morning',
'中午好': 'noon_one',
'午安': 'noon_two',
'下午好': 'afternoon',
'你好': 'hello',
'hello': 'hello',
'Hello': 'hello',
'hi': 'hello',
'Hi': 'hello',
'嗨': 'hello',
'好久不见': 'hello',
'晚上好': 'night_one',
'晚安': 'night_two',
'在吗': 'is_here',
'在不': 'is_here',
'在不在': 'is_here'
},
'type': 'dict'
},
{
'id': 17,
'desc': '打招呼字典',
'variable': 'HAIL_DICT',
'value': {
'hello': [
'Hi~你好哇~',
'你好你好,你有空陪我聊天了吗',
'嗯嗯,请多多关照~',
'你好呀,我是琥珀,来自瓦歌世界,初来地球,以后请多多关照~',
'你好呀,你今天不出去玩嘛'
],
'morning': [
'新的一天又开始咯~希望你可以元气满满哦~',
'早上好哦~早饭要次好哦',
'早上好哇~新的一天又开始啦~',
'早上好哦~',
'早上好~虽然还没睡醒,但要起床准备奋斗啦~',
'早上好~'
],
'noon_one': [
'中午好哦~吃个好饭好好犒劳自己~',
'中午好啊~',
'中午好啊~你准备吃什么呀',
'中午好啊~我刚吃完饭~',
'中午好,吃饭了没?'
],
'afternoon': [
'下午好啊~刚刚睡醒好困哦~',
'下午好啊~刚刚睡醒~',
'下午好啊~',
'下午好啊~累的时候可以跟我聊会天喔~'
],
'night_one': [
'晚上好呀~吃过饭了嘛',
'晚上好呀~辛苦了一天咯~是时候放松一下啦',
'晚上好呀~',
'晚上好呀~有空多找我聊天哦~',
'晚上好~'
],
'noon_two': [
'么么哒~午安~',
'么么哒~睡觉咯~午安~',
'么么哒~午觉完下午精神更好哦~午安~',
'么么哒~祝好梦~午安~',
'么么哒~一起睡哦~午安~',
'午安~'
],
'night_two': [
'么么哒~晚安~',
'么么哒~睡觉咯~晚安~',
'么么哒~早睡早起身体好~晚安~',
'么么哒~祝好梦~晚安~',
'么么哒~一起睡哦~晚安~',
'晚安~'
],
'is_here': [
'嗯嗯,我在~',
'嗯嗯,我一直在你身边~',
'嗯嗯,我一直在阿斯顿',
'我在~我在~',
'冒泡~',
'在呀',
'爱上的框架内'
]
},
'type': 'list_dict'
},
],
page_info: {
total: 4
}
}
}
total = body.data.page_info.total;
data = body.data.data.map((item, i) => {
item.key = (current - 1) * pageSize + i + 1;
if (item.type === 'dict') {
item.value = this.getDataFromProps(item.value);
}
return item;
});
} catch (error) {
message.error(`读取用户信息失败`);
}
this.setState({ data, total, isLoading: false });
}
onSearch(event) {
const { pageSize } = this.setState;
const searchText = event.target.value.replace(/^\s+|\s+$/g, '');
this.setState({ searchText, current: 1 });
if (this.delaySearch) {
clearTimeout(this.delaySearch);
}
this.delaySearch = setTimeout(e => this.getData(1, pageSize, searchText), 500);
}
onPageChange = (value) => {
this.setState({ current: value });
this.getData(value);
}
async onDelete(id, isActive) {
try {
// const resp = await gateway.jsonRequest('PATCH', `/api/v2/auth/users/${id}`, {
// data: {
// confirmed: !isActive,
// }
// });
// this.getData(this.current, this.pageSize, this.searchText);
} catch (error) {
message.error(`状态修改失败`);
}
}
toPropsData(data) {
const obj = {};
(Array.isArray(data) && data || []).forEach(item => {
obj[item[0]] = item[1];
})
return obj;
}
async handleOk() {
const { info } = this.state
this.setState({ confirmLoading: true });
if (info.type == 'dict') {
info.value = this.toPropsData(info.value);
}
try {
if (info.id) {
const resp = await fetch(`http://192.168.1.164:10086/bot/api/update_config`, {
method: 'POST',
headers: {
// 'Content-Type': 'application/json',
},
mode: 'cors',
body: JSON.stringify({ ...info }),
});
const body = await resp.json();
}
// else {
// itemInfo.maState = 1;
// itemInfo.password = '123456';
// const resp = await gateway.jsonRequest('POST', `/api/v2/auth/users/${itemInfo.id}`,
// {
// data: itemInfo
// }
// );
// }
this.setState({ visible: false });
} catch (error) {
message.error(`提交失败`);
}
this.setState({ confirmLoading: false });
this.getData();
}
handleCancel = () => {
this.setState({ visible: false, info: {} });
}
async onUserActiveChange(dataItem) {
dataItem.maState = !dataItem.isActive ? 1 : 0;
dataItem.maId = dataItem.id;
try {
// await gateway.jsonRequest(
// 'POST',
// `/datamanage/manage/sys/master/modify`,
// {
// data: dataItem
// }
// );
} catch (error) {
message.error(`更新失败`);
}
this.getData(this.current, this.pageSize, this.searchText);
}
renderTypeList() {
}
renderTypeValue(info) {
const { type, value } = info;
let renderTypeHTML = '';
switch (type) {
case 'text':
renderTypeHTML = <Input.TextArea rows={4} value={value} onChange={e => { info.value = e.target.value; this.setState({ info }) }} placeholder='输入 text 类型的值' />;
break;
case 'list':
renderTypeHTML = <ListGroup data={value} onChange={data => { info.value = data; this.setState({ info }); }} />
break;
case 'dict':
renderTypeHTML = <Dict data={value} onChange={data => { info.value = data; this.setState({ info }); }} />
break;
case 'list_dict':
renderTypeHTML = (Object.keys(value) || []).map(key =>
<Form.Item label={key}>
<ListGroup data={value[key]} onChange={data => { info.value[key] = data; this.setState({ info }); }} />
</Form.Item>)
break;
}
return renderTypeHTML;
}
render() {
const { history } = this.props;
const { isLoading, visible, confirmLoading, roleOptions, searchText, data, info, columns, current, pageSize, total,
} = this.state;
return (
<div className='users'>
<div className='topbar'>
<Input.Search
className='search'
placeholder='请输入姓名'
value={searchText}
onChange={this.onSearch}
/>
{/* <Button
className='button-add'
onClick={e => {
setVisible(true);
setInfo(info);
}}
>
<Icon type='plus' />
</Button> */}
</div>
<div className='list'>
<Table
loading={isLoading}
columns={columns}
dataSource={data}
pagination={{
current,
pageSize,
total,
onChange: this.onPageChange
}}
/>
</div>
<Drawer
title='信息维护'
width={720}
onClose={this.handleCancel}
visible={visible}
style={{
overflow: 'auto',
height: 'calc(100% - 108px)',
paddingBottom: '108px',
}}
>
<Form layout='vertical' hideRequiredMark>
<Row gutter={16}>
<Col span={20}>
<Form.Item label='描述'>
<Input placeholder='请输入描述' value={info.desc} onChange={e => { info.desc = e.target.value; this.setState({ info }) }} />
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item label='类型'>
<Select placeholder='请选择类型' value={info.type} onChange={value => { info.type = value; this.setState({ info }) }}>
<Select.Option value='text'>文本</Select.Option>
<Select.Option value='list'>列表</Select.Option>
<Select.Option value='dict'>字典</Select.Option>
<Select.Option value='list_dict'>字典列表</Select.Option>
</Select>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label='变量'>
<Input placeholder='请输入描述' value={info.variable} onChange={e => { info.variable = e.target.value; this.setState({ info }) }} />
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={20}>
<Form.Item label='值'>
{this.renderTypeValue(info)}
</Form.Item>
</Col>
</Row>
</Form>
<div
style={{
position: 'absolute',
left: 0,
bottom: 0,
width: '100%',
borderTop: '1px solid #e9e9e9',
padding: '10px 16px',
background: '#fff',
textAlign: 'right',
}}
>
<Button onClick={this.handleCancel} style={{ marginRight: 8 }}>
取消
</Button>
<Button confirmLoading={confirmLoading} onClick={e => this.handleOk()} type='primary'>
确认
</Button>
</div>
</Drawer>
</div>
);
}
}
export default withRouter(Home);

DevExtreme React Grid

Anyone know how to change the fontSize of the TableHeaderRow in a DevExtreme React Grid?
Here's an example of code from the website (https://devexpress.github.io/devextreme-reactive/react/grid/demos/featured/data-editing/) that I have been working with
import * as React from 'react';
import {
SortingState, EditingState, PagingState,
IntegratedPaging, IntegratedSorting,
} from '#devexpress/dx-react-grid';
import {
Grid,
Table, TableHeaderRow, TableEditRow, TableEditColumn,
PagingPanel, DragDropProvider, TableColumnReordering,
} from '#devexpress/dx-react-grid-material-ui';
import Paper from '#material-ui/core/Paper';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import Button from '#material-ui/core/Button';
import IconButton from '#material-ui/core/IconButton';
import Input from '#material-ui/core/Input';
import Select from '#material-ui/core/Select';
import MenuItem from '#material-ui/core/MenuItem';
import TableCell from '#material-ui/core/TableCell';
import DeleteIcon from '#material-ui/icons/Delete';
import EditIcon from '#material-ui/icons/Edit';
import SaveIcon from '#material-ui/icons/Save';
import CancelIcon from '#material-ui/icons/Cancel';
import { withStyles } from '#material-ui/core/styles';
import { ProgressBarCell } from '../../../theme-sources/material-ui/components/progress-bar-cell';
import { HighlightedCell } from '../../../theme-sources/material-ui/components/highlighted-cell';
import { CurrencyTypeProvider } from '../../../theme-sources/material-ui/components/currency-type-provider';
import { PercentTypeProvider } from '../../../theme-sources/material-ui/components/percent-type-provider';
import {
generateRows,
globalSalesValues,
} from '../../../demo-data/generator';
const styles = theme => ({
lookupEditCell: {
paddingTop: theme.spacing.unit * 0.875,
paddingRight: theme.spacing.unit,
paddingLeft: theme.spacing.unit,
},
dialog: {
width: 'calc(100% - 16px)',
},
inputRoot: {
width: '100%',
},
});
const AddButton = ({ onExecute }) => (
<div style={{ textAlign: 'center' }}>
<Button
color="primary"
onClick={onExecute}
title="Create new row"
>
New
</Button>
</div>
);
const EditButton = ({ onExecute }) => (
<IconButton onClick={onExecute} title="Edit row">
<EditIcon />
</IconButton>
);
const DeleteButton = ({ onExecute }) => (
<IconButton onClick={onExecute} title="Delete row">
<DeleteIcon />
</IconButton>
);
const CommitButton = ({ onExecute }) => (
<IconButton onClick={onExecute} title="Save changes">
<SaveIcon />
</IconButton>
);
const CancelButton = ({ onExecute }) => (
<IconButton color="secondary" onClick={onExecute} title="Cancel changes">
<CancelIcon />
</IconButton>
);
const commandComponents = {
add: AddButton,
edit: EditButton,
delete: DeleteButton,
commit: CommitButton,
cancel: CancelButton,
};
const Command = ({ id, onExecute }) => {
const CommandButton = commandComponents[id];
return (
<CommandButton
onExecute={onExecute}
/>
);
};
const availableValues = {
product: globalSalesValues.product,
region: globalSalesValues.region,
customer: globalSalesValues.customer,
};
const LookupEditCellBase = ({
availableColumnValues, value, onValueChange, classes,
}) => (
<TableCell
className={classes.lookupEditCell}
>
<Select
value={value}
onChange={event => onValueChange(event.target.value)}
input={(
<Input
classes={{ root: classes.inputRoot }}
/>
)}
>
{availableColumnValues.map(item => (
<MenuItem key={item} value={item}>
{item}
</MenuItem>
))}
</Select>
</TableCell>
);
export const LookupEditCell = withStyles(styles, { name: 'ControlledModeDemo' })(LookupEditCellBase);
const Cell = (props) => {
const { column } = props;
if (column.name === 'discount') {
return <ProgressBarCell {...props} />;
}
if (column.name === 'amount') {
return <HighlightedCell {...props} />;
}
return <Table.Cell {...props} />;
};
const EditCell = (props) => {
const { column } = props;
const availableColumnValues = availableValues[column.name];
if (availableColumnValues) {
return <LookupEditCell {...props} availableColumnValues={availableColumnValues} />;
}
return <TableEditRow.Cell {...props} />;
};
const getRowId = row => row.id;
class DemoBase extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
columns: [
{ name: 'product', title: 'Product' },
{ name: 'region', title: 'Region' },
{ name: 'amount', title: 'Sale Amount' },
{ name: 'discount', title: 'Discount' },
{ name: 'saleDate', title: 'Sale Date' },
{ name: 'customer', title: 'Customer' },
],
tableColumnExtensions: [
{ columnName: 'amount', align: 'right' },
],
rows: generateRows({
columnValues: { id: ({ index }) => index, ...globalSalesValues },
length: 12,
}),
sorting: [],
editingRowIds: [],
addedRows: [],
rowChanges: {},
currentPage: 0,
deletingRows: [],
pageSize: 0,
pageSizes: [5, 10, 0],
columnOrder: ['product', 'region', 'amount', 'discount', 'saleDate', 'customer'],
currencyColumns: ['amount'],
percentColumns: ['discount'],
};
const getStateDeletingRows = () => {
const { deletingRows } = this.state;
return deletingRows;
};
const getStateRows = () => {
const { rows } = this.state;
return rows;
};
this.changeSorting = sorting => this.setState({ sorting });
this.changeEditingRowIds = editingRowIds => this.setState({ editingRowIds });
this.changeAddedRows = addedRows => this.setState({
addedRows: addedRows.map(row => (Object.keys(row).length ? row : {
amount: 0,
discount: 0,
saleDate: new Date().toISOString().split('T')[0],
product: availableValues.product[0],
region: availableValues.region[0],
customer: availableValues.customer[0],
})),
});
this.changeRowChanges = rowChanges => this.setState({ rowChanges });
this.changeCurrentPage = currentPage => this.setState({ currentPage });
this.changePageSize = pageSize => this.setState({ pageSize });
this.commitChanges = ({ added, changed, deleted }) => {
let { rows } = this.state;
if (added) {
const startingAddedId = rows.length > 0 ? rows[rows.length - 1].id + 1 : 0;
rows = [
...rows,
...added.map((row, index) => ({
id: startingAddedId + index,
...row,
})),
];
}
if (changed) {
rows = rows.map(row => (changed[row.id] ? { ...row, ...changed[row.id] } : row));
}
this.setState({ rows, deletingRows: deleted || getStateDeletingRows() });
};
this.cancelDelete = () => this.setState({ deletingRows: [] });
this.deleteRows = () => {
const rows = getStateRows().slice();
getStateDeletingRows().forEach((rowId) => {
const index = rows.findIndex(row => row.id === rowId);
if (index > -1) {
rows.splice(index, 1);
}
});
this.setState({ rows, deletingRows: [] });
};
this.changeColumnOrder = (order) => {
this.setState({ columnOrder: order });
};
}
render() {
const {
classes,
} = this.props;
const {
rows,
columns,
tableColumnExtensions,
sorting,
editingRowIds,
addedRows,
rowChanges,
currentPage,
deletingRows,
pageSize,
pageSizes,
columnOrder,
currencyColumns,
percentColumns,
} = this.state;
return (
<Paper>
<Grid
rows={rows}
columns={columns}
getRowId={getRowId}
>
<SortingState
sorting={sorting}
onSortingChange={this.changeSorting}
/>
<PagingState
currentPage={currentPage}
onCurrentPageChange={this.changeCurrentPage}
pageSize={pageSize}
onPageSizeChange={this.changePageSize}
/>
<IntegratedSorting />
<IntegratedPaging />
<CurrencyTypeProvider for={currencyColumns} />
<PercentTypeProvider for={percentColumns} />
<EditingState
editingRowIds={editingRowIds}
onEditingRowIdsChange={this.changeEditingRowIds}
rowChanges={rowChanges}
onRowChangesChange={this.changeRowChanges}
addedRows={addedRows}
onAddedRowsChange={this.changeAddedRows}
onCommitChanges={this.commitChanges}
/>
<DragDropProvider />
<Table
columnExtensions={tableColumnExtensions}
cellComponent={Cell}
/>
<TableColumnReordering
order={columnOrder}
onOrderChange={this.changeColumnOrder}
/>
<TableHeaderRow showSortingControls />
<TableEditRow
cellComponent={EditCell}
/>
<TableEditColumn
width={120}
showAddCommand={!addedRows.length}
showEditCommand
showDeleteCommand
commandComponent={Command}
/>
<PagingPanel
pageSizes={pageSizes}
/>
</Grid>
<Dialog
open={!!deletingRows.length}
onClose={this.cancelDelete}
classes={{ paper: classes.dialog }}
>
<DialogTitle>
Delete Row
</DialogTitle>
<DialogContent>
<DialogContentText>
Are you sure to delete the following row?
</DialogContentText>
<Paper>
<Grid
rows={rows.filter(row => deletingRows.indexOf(row.id) > -1)}
columns={columns}
>
<CurrencyTypeProvider for={currencyColumns} />
<PercentTypeProvider for={percentColumns} />
<Table
columnExtensions={tableColumnExtensions}
cellComponent={Cell}
/>
<TableHeaderRow />
</Grid>
</Paper>
</DialogContent>
<DialogActions>
<Button onClick={this.cancelDelete} color="primary">
Cancel
</Button>
<Button onClick={this.deleteRows} color="secondary">
Delete
</Button>
</DialogActions>
</Dialog>
</Paper>
);
}
}
export default withStyles(styles, { name: 'ControlledModeDemo' })(DemoBase);
The font size of the text labelling the columns (e.g. product, region, amount) is fixed, and I see no parameters that can change it. Any ideas?
I think there are a few ways around this, the way I have used is having a fully controlled component.
Looks a little like this
<TableHeaderRow cellComponent={this.ExampleHeaderCell} />
Where ExampleHeaderCell is a component that would look something like this
ExampleHeaderCell = (props: any) => (<TableHeaderRow.Cell
className={exampleClass}
{...props}
key={column.name}
getMessage={() => column.title}
/>)
From there you can pass it a class as shown with exampleClass
You can take this further and have it customised for a particular column.
ExampleHeaderCells = (props: any) => {
const exampleClass = css({ backgroundColor: "blue" })
const { column } = props
if (column.name === "name") {
return (
<TableHeaderRow.Cell
className={exampleClass}
{...props}
key={column.name}
getMessage={() => column.title}
/>
)
}
return <TableHeaderRow.Cell {...props} key={column.name} getMessage={() => column.title} />
}
The example above is returning a specific cell with the exampleClass if the column name is equal to "name". Otherwise it just returns the regular TableHeaderRow.Cell

Categories

Resources