CK Editor Validation using React Hook Form - javascript

I have a nextJs project in which i use a CKeditor react component and I use React hook form as my validation library
my code for the main page is as shown below
import React from "react";
import { Paper } from "#material-ui/core";
import { makeStyles, withStyles } from "#material-ui/core/styles";
import { useForm } from "react-hook-form";
import dynamic from "next/dynamic";
const Editor = dynamic(() => import("../../components/Form/Editor"), {
ssr: false,
});
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
"& > *": {
margin: theme.spacing(1),
},
},
paper: {
padding: theme.spacing(2),
textAlign: "center",
color: theme.palette.text.secondary,
},
}));
function create2() {
const classes = useStyles();
const { errors, control, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<Paper className={classes.paper}>
<form onSubmit={handleSubmit(onSubmit)}>
<Editor
control={control}
onReady={(editor) => {
// You can store the "editor" and use when it is needed.
// console.log("Editor is ready to use!", editor);
editor.editing.view.change((writer) => {
writer.setStyle(
"height",
"200px",
editor.editing.view.document.getRoot()
);
});
}}
defaultValue=""
name="question"
value={formValues.question}
rules={{ required: "Question Field is required" }}
errors={errors}
/>
<input type="submit" />
</form>
</Paper>
);
}
export default create2;
and My Editor component is as shown below
import React, { useEffect, useState } from "react";
import { CKEditor } from "#ckeditor/ckeditor5-react";
import { makeStyles, useTheme } from "#material-ui/core/styles";
import ClassicEditor from "#ckeditor/ckeditor5-build-classic";
import { Controller } from "react-hook-form";
//import ClassicEditor from 'ckeditor5-classic-with-mathtype';
const useStyles = makeStyles((theme) => ({
formControl: {
// margin: theme.spacing(1),
minWidth: 120,
fullWidth: true,
},
errordiv: {
width: "100%",
border: "1px solid red",
},
errorText: {
width: "100%",
color: "red",
display: "flex",
padding: "2px 10px",
},
}));
const Editor = (props) => {
const classes = useStyles();
const [isLayoutReady, setIsLayoutReady] = useState(true);
const [error, setError] = useState({ error: false, helperText: "" });
const errorObj = props.errors || "";
useEffect(() => {
const errorVal = errorObj ? errorObj[props.name] : "";
if (errorVal) {
setError({ ...error, error: true, helperText: errorVal.message });
} else {
setError({ error: false, helperText: "" });
}
}, [errorObj[props.name]]);
useEffect(() => {
setIsLayoutReady(true);
}, []);
return (
<div>
<div className={error.error ? classes.errordiv : ""}>
{isLayoutReady ? (
<Controller
as={CKEditor}
{...props}
config={{
ckfinder: {
// Upload the images to the server using the CKFinder QuickUpload command.
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": "true",
uploadUrl:
"https://run.mocky.io/v3/5125188a-47c1-4138-8871-8837d5ab8764",
},
toolbar: {
items: [
"heading",
"|",
"bold",
"italic",
"link",
"bulletedList",
"numberedList",
"|",
"indent",
"outdent",
"|",
"imageUpload",
"blockQuote",
"insertTable",
"mediaEmbed",
"undo",
"redo",
"fontColor",
"fontSize",
"fontFamily",
"MathType",
"ChemType",
],
},
language: "en",
}}
editor={ClassicEditor}
/>
) : null}
</div>
<div className={classes.errorText}>
{error.error ? error.helperText : ""}
</div>
</div>
);
};
export default Editor;
I am getting the validation alert at the first time i click the submit button with required rule but the validation is not returning if i clear the ck editor value and also the out put data I am getting on submit is some object
question: tsname: "change:data"off: ƒ t()path: [qg]source: qg {model: Fp, version: 19, history: Vg, selection: Cf, roots: vs, …}stop: ƒ t()proto: Object__proto__: Object
Can Anybody help me with this error.

Try this...
<CKEditor
data={ textValue }
onChange={(event, editor) => {
const data = editor.getData();
setTextValue(data);
}}
/>
Cheer me !!

Related

How to create a custom MUI-Datatables search box with outlined style

Default UI from MUI-Datatables v4.3.0 like this :
And i'am want to make this style :
For information i am using this package :
"#mui/material": "^5.11.4"
"mui-datatables": "^4.3.0"
How to make The TextField or Input with outlined style.
Based of example MUI-Databales from this link :
https://codesandbox.io/s/github/gregnb/mui-datatables
I have a answer and solution from my case.
My Component CustomSearchRender.js :
import React from "react";
import Grow from "#mui/material/Grow";
import TextField from "#mui/material/TextField";
import { withStyles } from "tss-react/mui";
import { SearchOutlined } from "#mui/icons-material";
import InputAdornment from "#mui/material/InputAdornment";
const defaultSearchStyles = (theme) => ({
searchText: {
flex: "0.8 0",
marginTop: 10,
},
searchIcon: {
"&:hover": {
color: theme.palette.error.main,
},
},
});
const CustomSearchRender = (props) => {
const { classes, onSearch, searchText } = props;
const handleTextChange = (event) => {
onSearch(event.target.value);
};
return (
<Grow appear in={true} timeout={300}>
<TextField
placeholder="Search"
size="medium"
className={classes.searchText}
value={searchText || ""}
onChange={handleTextChange}
fullWidth
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchOutlined />
</InputAdornment>
),
}}
/>
</Grow>
);
};
export default withStyles(CustomSearchRender, defaultSearchStyles, {
name: "CustomSearchRender",
});
And put into options :
const options = {
selectableRows: "none",
filterType: "textField",
responsive: "simple",
confirmFilters: false,
jumpToPage: false,
download: false,
print: false,
enableNestedDataAccess: ".",
textLabels: {
body: {
noMatch: loading ? (
<TableSkeleton variant="h4" length={10} />
) : (
"Maaf, tidak ada data untuk ditampilkan"
),
},
},
searchOpen: true,
searchAlwaysOpen: true,
searchPlaceholder: "Search keyword",
customSearchRender: (searchText, handleSearch) => {
return (
<CustomSearchRender searchText={searchText} onSearch={handleSearch} />
);
},
};
And finnally the result like this :

How can I insert a basic Search bar in Material UI Data Grid?

For example: refer this sample data grid.
Want to add a basic search bar like:here
How can I add in Data Grid MUI.
Please guide.
Just create a search bar using a label and text input. Aftermath you can update your grid according to the string in your search box by the search filtered data.
In datagrid there is prop components and Toolbar in that prop to pass whatever component you want.
components={{ Toolbar: QuickSearchToolbar }}
and pass props from
componentsProps={{
toolbar: {
value: searchText,
onChange: (event: React.ChangeEvent<HTMLInputElement>) => handleSearch
},
}}
This is a good example to check out:
import * as React from 'react';
import IconButton from '#mui/material/IconButton';
import TextField from '#mui/material/TextField';
import {
DataGrid,
GridToolbarDensitySelector,
GridToolbarFilterButton,
} from '#mui/x-data-grid';
import { useDemoData } from '#mui/x-data-grid-generator';
import ClearIcon from '#mui/icons-material/Clear';
import SearchIcon from '#mui/icons-material/Search';
import { createTheme } from '#mui/material/styles';
import { createStyles, makeStyles } from '#mui/styles';
function escapeRegExp(value: string): string {
return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
const defaultTheme = createTheme();
const useStyles = makeStyles(
(theme) =>
createStyles({
root: {
padding: theme.spacing(0.5, 0.5, 0),
justifyContent: 'space-between',
display: 'flex',
alignItems: 'flex-start',
flexWrap: 'wrap',
},
textField: {
[theme.breakpoints.down('xs')]: {
width: '100%',
},
margin: theme.spacing(1, 0.5, 1.5),
'& .MuiSvgIcon-root': {
marginRight: theme.spacing(0.5),
},
'& .MuiInput-underline:before': {
borderBottom: `1px solid ${theme.palette.divider}`,
},
},
}),
{ defaultTheme },
);
interface QuickSearchToolbarProps {
clearSearch: () => void;
onChange: () => void;
value: string;
}
function QuickSearchToolbar(props: QuickSearchToolbarProps) {
const classes = useStyles();
return (
<div className={classes.root}>
<div>
<GridToolbarFilterButton />
<GridToolbarDensitySelector />
</div>
<TextField
variant="standard"
value={props.value}
onChange={props.onChange}
placeholder="Search…"
className={classes.textField}
InputProps={{
startAdornment: <SearchIcon fontSize="small" />,
endAdornment: (
<IconButton
title="Clear"
aria-label="Clear"
size="small"
style={{ visibility: props.value ? 'visible' : 'hidden' }}
onClick={props.clearSearch}
>
<ClearIcon fontSize="small" />
</IconButton>
),
}}
/>
</div>
);
}
export default function QuickFilteringGrid() {
const { data } = useDemoData({
dataSet: 'Commodity',
rowLength: 100,
maxColumns: 6,
});
const [searchText, setSearchText] = React.useState('');
const [rows, setRows] = React.useState<any[]>(data.rows);
const requestSearch = (searchValue: string) => {
setSearchText(searchValue);
const searchRegex = new RegExp(escapeRegExp(searchValue), 'i');
const filteredRows = data.rows.filter((row: any) => {
return Object.keys(row).some((field: any) => {
return searchRegex.test(row[field].toString());
});
});
setRows(filteredRows);
};
React.useEffect(() => {
setRows(data.rows);
}, [data.rows]);
return (
<div style={{ height: 400, width: '100%' }}>
<DataGrid
components={{ Toolbar: QuickSearchToolbar }}
rows={rows}
columns={data.columns}
componentsProps={{
toolbar: {
value: searchText,
onChange: (event: React.ChangeEvent<HTMLInputElement>) =>
requestSearch(event.target.value),
clearSearch: () => requestSearch(''),
},
}}
/>
</div>
);
}

How to render multiple notifications with help of notistack in react?

I'm trying to use React's 'notistack' module to display several notifications as a stack. However, it appears that I am making a mistake, as Whenever I receive a warning:
react_devtools_backend.js:3973 Warning: Cannot update during an existing state transition (such as within render)
I'm using the state here to set the Response messages(they are coming from api), which are an array of notifications. Once the alerts have been rendered, I should also make the state of the Response messages an empty array.
renderPage.js
import React, { useState, useContext } from "react";
import axios from "axios";
import Button from "#mui/material/Button";
import Typography from "#mui/material/Typography";
import { SnackbarProvider } from "notistack";
import Zoom from "#material-ui/core/Slide";
import Notification from "../../components/Notification";
import { handleResponse } from "../../services/responseHandler";
import { SelectedRunnerContext } from "./RunnersPage";
export function RunnersForm() {
const { selected } = useContext(SelectedRunnerContext);
const [responseMessages, setResponseMessages] = useState([]);
const notistackRef = React.createRef();
const onClickDismiss = (key) => () => {
notistackRef.current.closeSnackbar(key);
};
const MakePostRequest = (event) => {
event.preventDefault();
setResponseMessages([]);
const data = {
selected_runners: selected,
};
const requestOptions = {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: data,
};
axios
.post("/messages", requestOptions)
.then((response) => {
const result = handleResponse(response.data);
setResponseMessages(result);
})
.catch((error) => {
console.log(error);
});
};
return (
<>
<Button onClick={MakePostRequest}>Post</Button>
{!!responseMessages.length && (
<SnackbarProvider
maxSnack={4}
ref={notistackRef}
dense
preventDuplicate
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
TransitionComponent={Zoom}
sx={{
width: 700,
}}
style={{
fontSize: 15,
fontWeight: 700,
}}
action={(key) => (
<Button onClick={onClickDismiss(key)}>
<Typography
variant="subtitle2"
style={{
fontWeight: 600,
fontSize: 16,
color: "#00579b",
}}
>
Dismiss
</Typography>
</Button>
)}
>
<Notification messages={responseMessages} />
</SnackbarProvider>
)}
</>
);
}
Notification.js
import React, { Fragment } from "react";
import { useSnackbar } from "notistack";
export default function Notification(props) {
const { enqueueSnackbar } = useSnackbar();
const { messages } = props;
const options = {
variant: "success",
autoHideDuration: 6000,
transitionDuration: { enter: 400, exit: 400 },
};
messages.forEach((msg) => {
enqueueSnackbar(msg, options);
});
return <></>;
}
I've solved the issue by wrapping up the App with the Notification provider:
import React from "react";
import { useSnackbar } from "notistack";
import Button from "#mui/material/Button";
import { SnackbarProvider } from "notistack";
import Typography from "#mui/material/Typography";
function MakeGlobal() {
const { enqueueSnackbar } = useSnackbar();
window.enqueueSnackbar = enqueueSnackbar;
return null;
}
export function displayNotification(messages) {
const options = {
variant: "success",
autoHideDuration: 6000,
transitionDuration: { enter: 400, exit: 400 },
};
messages.forEach((msg) => {
window.enqueueSnackbar(msg, options);
});
}
export function Provider({ children }) {
const notistackRef = React.createRef();
const onClickDismiss = (key) => () => {
notistackRef.current.closeSnackbar(key);
};
return (
<SnackbarProvider
maxSnack={4}
ref={notistackRef}
dense
preventDuplicate
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
sx={{
width: 700,
}}
style={{
fontSize: 15,
fontWeight: 700,
}}
action={(key) => (
<Button onClick={onClickDismiss(key)}>
<Typography
variant="subtitle2"
style={{
fontWeight: 600,
fontSize: 16,
color: "#00579b",
}}
>
Dismiss
</Typography>
</Button>
)}
>
<MakeGlobal />
{children}
</SnackbarProvider>
);
}
App.js
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<BrowserRouter>
<snackbar.Provider>
<AppRoutes />
<CssBaseline />
</snackbar.Provider>
</BrowserRouter>
);

How to add Edit material-ui icons in data-grid component column component

I am using material-ui data-grid and I want to show Edit material-ui icons against each row. But in data-grid, we don't have any props that can be used for the same. Below is the source code:
import React, {useState, useEffect} from "react";
import { DataGrid } from "#material-ui/data-grid";
import { Edit } from "#material-ui/icons";
const MyComponent= () => {
return (
<DataGrid
rows={[{name: "ABC", email: "xyz#gmail.com"}]}
columns={[{ field: "name", headerName: "Name" }, { field: "email", headerName: "Email" }]}
/>
)
};
export default MyComponent;
import React, {useState, useEffect} from "react";
import { FormControlLabel, IconButton } from '#material-ui/core';
import { DataGrid } from "#material-ui/data-grid";
import EditIcon from '#material-ui/icons/Edit';
import { blue } from '#material-ui/core/colors';
const MatEdit = ({ index }) => {
const handleEditClick = () => {
// some action
}
return <FormControlLabel
control={
<IconButton color="secondary" aria-label="add an alarm" onClick={handleEditClick} >
<EditIcon style={{ color: blue[500] }} />
</IconButton>
}
/>
};
const MyComponent= () => {
const rows = [{ id: 1, name: "ABC", email: "xyz#gmail.com" }];
const columns=[
{ field: "name", headerName: "Name" },
{ field: "email", headerName: "Email" },
{
field: "actions",
headerName: "Actions",
sortable: false,
width: 140,
disableClickEventBubbling: true,
renderCell: (params) => {
return (
<div className="d-flex justify-content-between align-items-center" style={{ cursor: "pointer" }}>
<MatEdit index={params.row.id} />
</div>
);
}
}
];
return (
<div style={{ height: 500, width: 500 }}>
<DataGrid rows={rows} columns={columns} />
</div>
)
};
export default MyComponent;
Click here to see the demo.
Yeah, you can add renderCell to you column as
You can create custom column definitions and provide custom render functions for cells.
See https://material-ui.com/components/data-grid/rendering/ for details.

React Beginner Question: Textfield Losing Focus On Update

I wrote a component that is supposed to list out a bunch of checkboxes with corresponding textfields. When you click on the checkboxes, or type in the fields it's meant to update state.
The textbox is working ok, but when I type in the fields, it updates state ok, but I lose focus whenever I tap the keyboard.
I realized this is probably due to not having keys set, so I added keys to everything but it still is losing focus. At one point I tried adding in stopPropegation on my events because I thought maybe that was causing an issue?? I'm not sure.. still learning...didn't seem to work so I removed that part too.
Still can't seem to figure out what is causing it to lose focus... does anyone have any advice/solves for this issue?
I consolidated my code and cut out the unnecessary bits to make it easier to read. There are three relevant JS files.. please see below:
I'm still a beginner/learning so if you have useful advice related to any part of this code, feel free to offer. Thanks!
App.js
import React, { Component } from 'react';
import Form from './Form'
class App extends Component {
constructor() {
super();
this.state = {
mediaDeliverables: [
{label: 'badf', checked: false, quantity:''},
{label: 'adfadf', checked: false, quantity:''},
{label: 'adadf', checked: false, quantity:''},
{label: 'addadf', checked: false, quantity:''},
{label: 'adfdes', checked: false, quantity:''},
{label: 'hghdgs', checked: false, quantity:''},
{label: 'srtnf', checked: false, quantity:''},
{label: 'xfthd', checked: false, quantity:''},
{label: 'sbnhrr', checked: false, quantity:''},
{label: 'sfghhh', checked: false, quantity:''},
{label: 'sssddrr', checked: false, quantity:''}
]
}
}
setMediaDeliverable = (value, index) => {
let currentState = this.getStateCopy();
currentState.mediaDeliverables[index] = value;
this.setState(currentState);
}
getStateCopy = () => Object.assign({}, this.state);
render() {
return (
<div className="App">
<Form
key="mainForm"
mediaDeliverablesOptions={this.state.mediaDeliverables}
setMediaDeliverable={this.setMediaDeliverable}
/>
</div>
);
}
}
export default App;
Form.js
import React from 'react';
import { makeStyles, useTheme } from '#material-ui/core/styles';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
import FormGroup from '#material-ui/core/FormGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
import MediaDeliverablesCheckBox from './MediaDeliverablesCheckBox';
const useStyles = makeStyles(theme => ({
container: {
display: 'inline-block',
flexWrap: 'wrap',
},
root: {
display: 'inline-block',
flexWrap: 'wrap',
maxWidth: 600,
textAlign: 'left',
},
extendedIcon: {
marginRight: theme.spacing(1),
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300,
},
textField: {
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
width: 370,
},
dense: {
marginTop: 19,
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
margin: 2,
},
noLabel: {
marginTop: theme.spacing(3),
},
}));
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
function getStyles(name, accountName, theme) {
// console.log('>> [form.js] (getStyles) ',accountName)
return {
fontWeight:
accountName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}
export default function Form(props) {
const mediaDeliverablesOptions = props.mediaDeliverablesOptions;
const classes = useStyles();
const theme = useTheme();
const CheckboxGroup = ({ values, label, onChange }) => (
<FormControl component="fieldset">
<FormLabel component="legend">{label}</FormLabel>
<FormGroup>
{values.map((value, index) => (
<FormControlLabel
key={index}
control={
<Checkbox
checked={value.checked}
onChange={onChange(index)}
/>
}
label={value.label}
/>
))}
</FormGroup>
</FormControl>
);
const MediaDeliverableCheckBoxList = ({values, label}) => (
<FormControl component="fieldset">
<FormLabel component="legend">{label}</FormLabel>
<FormGroup>
{values.map((value, index) => (
<MediaDeliverablesCheckBox
key={index}
mediaDeliverablesOptions={value}
onMediaDeliverableChange={onMediaDeliverableChange(index)}
/>
))}
</FormGroup>
</FormControl>
);
const onCheckBoxChange = index => ({ target: { checked } }) => {
const newValues = [...values];
const value = values[index];
newValues[index] = { ...value, checked };
props.setDesignOrDigital(newValues);
};
const onMediaDeliverableChange = index => (deliverableData, e) => {
props.setMediaDeliverable(deliverableData, index);
}
return (
<div className={classes.root}>
<MediaDeliverableCheckBoxList
label="Please Choose Deliverables:"
values={mediaDeliverablesOptions}
key="media-deliverable-checkbox-list"
/>
</div>
);
}
MediaDeliverablesCheckbox.js
import React from 'react';
import Checkbox from '#material-ui/core/Checkbox';
import { makeStyles, useTheme } from '#material-ui/core/styles';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
import FormGroup from '#material-ui/core/FormGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import TextField from '#material-ui/core/TextField';
export default function MediaDeliverablesCheckBox(props) {
let deliverableData = Object.assign({}, props.mediaDeliverablesOptions);
const onCheckBoxChange = (e) => {
deliverableData.checked = e.target.checked;
props.onMediaDeliverableChange(deliverableData, e);
}
const onQuantityChange = (e) => {
deliverableData.quantity = e.target.value;
props.onMediaDeliverableChange(deliverableData, e);
}
const CheckboxGroup = ({ value, label }) => (
<FormControl component="fieldset">
<FormGroup>
<FormControlLabel
control={
<Checkbox
key={props.index}
checked={value.checked}
onChange={onCheckBoxChange}
/>
}
label={label}
/>
</FormGroup>
</FormControl>
);
return(
<div className="MediaDeliverablesCheckBox">
<CheckboxGroup
key={props.index}
label={props.mediaDeliverablesOptions.label}
value={props.mediaDeliverablesOptions}
/>
<TextField
key={'tf'+props.index}
id={'quantity-'+props.index}
label="Quantity"
placeholder="How many do you need?"
multiline
variant="outlined"
value={props.mediaDeliverablesOptions.quantity}
onChange={onQuantityChange}
fullWidth
/>
</div>
);
}
Updated Form.js based on recommended edits by Ryan C.
import React from 'react';
import { makeStyles, useTheme } from '#material-ui/core/styles';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
import FormGroup from '#material-ui/core/FormGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
import MediaDeliverablesCheckBox from './MediaDeliverablesCheckBox';
const useStyles = makeStyles(theme => ({
container: {
display: 'inline-block',
flexWrap: 'wrap',
},
root: {
display: 'inline-block',
flexWrap: 'wrap',
maxWidth: 600,
textAlign: 'left',
},
extendedIcon: {
marginRight: theme.spacing(1),
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300,
},
textField: {
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
width: 370,
},
dense: {
marginTop: 19,
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
margin: 2,
},
noLabel: {
marginTop: theme.spacing(3),
},
}));
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
function getStyles(name, accountName, theme) {
return {
fontWeight:
accountName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}
// Failed to compile
// ./src/Form.js
// Line 86: Parsing error: Unexpected token, expected ","
// 84 |
// 85 | const MediaDeliverableCheckBoxList = ({values, label, onMediaDeliverableChange}) => (
// > 86 | {values.map((value, index) => (
// | ^
// 87 | <MediaDeliverablesCheckBox
// 88 | key={index}
// 89 | index={index}
// This error occurred during the build time and cannot be dismissed.
const MediaDeliverableCheckBoxList = ({values, label, onMediaDeliverableChange}) => (
{values.map((value, index) => (
<MediaDeliverablesCheckBox
key={index}
index={index}
mediaDeliverablesOptions={value}
onMediaDeliverableChange={onMediaDeliverableChange(index)}
/>
))}
);
export default function Form(props) {
const mediaDeliverablesOptions = props.mediaDeliverablesOptions;
const classes = useStyles();
const theme = useTheme();
const CheckboxGroup = ({ values, label, onChange }) => (
<FormControl component="fieldset">
<FormLabel component="legend">{label}</FormLabel>
<FormGroup>
{values.map((value, index) => (
<FormControlLabel
key={index}
control={
<Checkbox
checked={value.checked}
onChange={onChange(index)}
/>
}
label={value.label}
/>
))}
</FormGroup>
</FormControl>
);
const onCheckBoxChange = index => ({ target: { checked } }) => {
const newValues = [...values];
const value = values[index];
newValues[index] = { ...value, checked };
props.setDesignOrDigital(newValues);
};
const onMediaDeliverableChange = index => (deliverableData, e) => {
props.setMediaDeliverable(deliverableData, index);
}
return (
<div className={classes.root}>
<MediaDeliverableCheckBoxList
onMediaDeliverableChange={onMediaDeliverableChange}
/>
</div>
);
}
I see two main issues:
How you are defining your different components (nesting component types)
Not passing the index prop through to components that are expecting it
You have the following structure (leaving out details that are not directly related to my point):
export default function Form(props) {
const onMediaDeliverableChange = index => (deliverableData, e) => {
props.setMediaDeliverable(deliverableData, index);
}
const MediaDeliverableCheckBoxList = ({values, label}) => (
<FormGroup>
{values.map((value, index) => (
<MediaDeliverablesCheckBox key={index} onMediaDeliverableChange={onMediaDeliverableChange(index)}/>
))}
</FormGroup>
);
return (
<MediaDeliverableCheckBoxList/>
);
}
The function MediaDeliverableCheckBoxList represents the component type used to render the <MediaDeliverableCheckBoxList/> element. Whenever Form is re-rendered due to props or state changing, React will re-render its children. If the component type of a particular child is the same (plus some other criteria such as key being the same if specified), then it will update the existing DOM node(s). If the component type of a particular child is different, then the corresponding DOM nodes will be removed and new ones added to the DOM.
By defining the MediaDeliverableCheckBoxList component type within the Form function, you are causing that component type to be different on every render. This will cause all of the DOM nodes to be replaced rather than just updated and this will cause the focus to go away when the DOM node that previously had focus gets removed. It will also cause performance to be considerably worse.
You can fix this by moving this component type outside of the Form function and then adding any additional props that are needed (e.g. onMediaDeliverableChange) to convey the context known inside of Form. You also need to pass index as a prop to MediaDeliverablesCheckBox since it is using it.
const MediaDeliverableCheckBoxList = ({values, label, onMediaDeliverableChange}) => (
<FormGroup>
{values.map((value, index) => (
<MediaDeliverablesCheckBox key={index} index={index} onMediaDeliverableChange={onMediaDeliverableChange(index)}/>
))}
</FormGroup>
);
export default function Form(props) {
const onMediaDeliverableChange = index => (deliverableData, e) => {
props.setMediaDeliverable(deliverableData, index);
}
return (
<MediaDeliverableCheckBoxList onMediaDeliverableChange={onMediaDeliverableChange}/>
);
}
You have this same issue with CheckboxGroup and possibly other components as well.
This issue is solely because of your key in the TextField. You must ensure that the key remains the same on every update. Otherwise you would the face the current issue.

Categories

Resources