We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. Image for the cookie policy date

Grid in inline-edit mode crashes when another component is rendered or state changes during editing.

Hello. I'm using Syncfusion Grid in a React functional component with inline-edit mode.
While I'm editing a row, if the state changes (ex. I want to save some value during editing) or I try to render anything (ex. a toast alert), grid crashes and in chrome's console I get the following alert:
"Warning: unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering."


Also when I'm not doing any edits, if another component renders or triggers a state change, grid refreshes. If I log args of actionBegin attribute, I get the following:
  1. {requestType: "refresh", name: "actionBegin"}
    1. name"beforeFragAppend"
    2. promiseundefined
    3. requestType"refresh"

Is there a way to resolve the first issue? And regarding the second query, why is this happening and how to prevent it?
Thank you in advance!

15 Replies 1 reply marked as answer

RR Rajapandi Ravi Syncfusion Team May 13, 2021 11:39 AM UTC

Hi Spyros, 

Greetings from syncfusion support 

In this below demo, Normal mode is enabled for editing. You can start edit any row by double clicking on it or clicking on toolbar’sEdit button, then the currently selected row will be changed to edited state. You can change the row values and save edited data to datasource. 


In your query you are mentioned that “render anything (ex. a toast alert)”. We need more information for our clarification. So please share the below details that would be helpful for us to provide better solution. 

1)     Please confirm if you are render any custom component in edit form and if yes please share the details about how you render that component. Please share your requirement with detail description.  

2)    Please share the complete Grid rendering code. 

3)    Please share issue replication procedure step by step. 

4)    Please share the issue scenario in video format. 

Regards, 
Rajapandi R


SP Spyros May 13, 2021 06:32 PM UTC

Dear Rajapandi R,

Thank you for your reply. I think you misunderstood the word "state" I mentioned in my original post, I was referring to the React state, not Grid editing state, I could have made it more clear, sorry.

As for more details on how to reproduce the issue, for simplicity I will explain how I do a state change (since it has the same effects as rendering a complete component).

1) In my functional React component, I declare a state variable.
const [iconFileName, setIconFileName] = useState(null);
2) In one of my grid's columns I display an icon and have a button to upload new icon while editing, by rendering Syncfusion's UploaderComponent (Grid's "editTemplate" attribute). I also have a paragraph under it that displays the value of the state variable.

Grid's column:

<ColumnDirective field='icon' headerText="Icon" template={iconImage} editTemplate={iconImageUploader} width='100' textAlign="center" />

The editTemplate of that column:

const iconImageUploader = args => {
    return (
        <div>
            <UploaderComponent id="uploader"
                dropArea={false}
                autoUpload={true}
                multiple={false}
                asyncSettings={uploadPath}
                allowedExtensions='.png
                success={onUploadSuccess}
            />
            <p>{iconFileName}</p>
        </div>
    );
};

3) Once I select a file and it's uploaded, function onUploadSuccess runs. The job of that function is to change the state variable that shows the filename, so that it will be shown in the paragraph below the "Browse" button of the uploader, while still being on editing mode. (I have set some dummy static text to keep the example simple).

const onUploadSuccess = args => {
    setIconFileName("filename-goes-here");
}

4) Setup is done, now I have my grid running, when I try to edit an entry the "Browse" button of the uploader is shown and I can upload a picture. Once upload is finished (I can see the file on the server) and "onUploadSuccess" runs, the grid exits editing mode and crashes. In Chrome's developer console I see the following error, followed by the complete trace to the root of my React application:

Warning: unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering.
    at GridComponent (webpack-internal:///./node_modules/@syncfusion/ej2-react-grids/src/grid/grid.component.js:33:28)
    .............

Please note that this does not have to do with the UploaderComponent at all but rather with editing the React state. If I use Grid's "actionComplete" attribute to make any change to the state while in editing mode (requestType == "beginEdit"), the result is exactly the same, grid exits editing mode and crashes, followed by the same error.

If you need any further information please let me know.
Best regards!


RR Rajapandi Ravi Syncfusion Team May 18, 2021 09:48 AM UTC

Hi Spyros, 

Based on your query we have prepared a sample which was same as your application scenario and try to reproduce your reported problem. But it was unsuccessful. Please refer the below sample for more information. 


If you still face the issue, please share the below details that will be helpful for us to provide better solution.  

1)      Please share your syncfusion package version 

2)       Please share any issue reproducible sample or try to reproduce the issue with our above attached sample. 

3)      Share the issue replication procedure step by step. 

4)      Share the issue scenario in video format. 

Regards,
Rajapandi R



SP Spyros May 18, 2021 06:17 PM UTC

Dear Rajapandi R,

Thank you for taking the time to create an example project for my use case.
I noticed that in the example project, you did create a paragraph that displays the value of state variable "iconFileName", although the value of that variable never changes, the use case I want is to change the value of that variable while in grid editing mode.

You can achieve that by modifying a couple lines of your code of file "Home.tsx":
  1. In line 66, where you define the "iconFileName" state variable, change it to
    const [iconFileName, setIconFileName] = useState('Initial value');
    so that we can assign a new value to the variable.
  2. After line 32, where you define what happens once image upload is done, add the following line which will set the value of "iconFileName" state variable as the name of the file that was just uploaded:
    setIconFileName(props.file.name);
Now, once a new image is uploaded, the expected behavior is for the paragraph under the "Browse" button to change from "Initial value" to the name of the file that was just uploaded.
What happens though, is that grid exits editing mode, and the following error is shown in Chrome's console:
Warning: unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering.
    in GridComponent (created by Home)
    in div (created by Home)
    in Home (created by App)
    in div (created by App)
    in App

I don't really understand why it's happening. Does the Grid Component not use the React APIs? To my understanding React would be able to handle such a situation of state changing while a rendering is ongoing (if this is what happens in my case).

Looking forward to your thoughts.
Best regards!


RR Rajapandi Ravi Syncfusion Team May 19, 2021 10:31 AM UTC

Hi Spyros, 

Thanks for the update 

We have analyzed your query and we could see that you are changing the state variable in UploadSuccess event. While changing the state in this event, the grid will start refresh. So, it will cause the form to be closed. To overcome the problem, we have changed the paragraph tag value by using the dom elements in UploadSuccess event and then we change the state variable in actionBegin event of Grid. Please refer the below code example and sample for more information. 

 
function Home() { 
  const begin = (props: any) => { //actionBegin event of Grid 
    if (props.requestType === "save") { 
      props.data.Image = readString; //set the uploaded image string to the column field it helps to refresh the data and display the uploaded image in the template column 
      setIconFileName((document.getElementsByClassName('e-upload')[0] as any).nextElementSibling.innerText); 
      //change the state after updating the image data 
    } 
  } 
    const onUploadSuccess = (props: any) => { 
//change the paragraph tag variable by using dom element 
      (document.getElementsByClassName('e-upload')[0] as any).nextElementSibling.innerText = props.file.name; 
    if(props.operation === 'upload') { 
        // Raw file of the uploaded image is retrieved 
        const file = props.file.rawFile; 
        // File reader is initialized and the raw file is read as URL 
        const reader = new FileReader(); 
        reader.readAsDataURL(file); 
        // The base64 result is retrieved on load success 
        // This value is stored in global variable and returned in the read function of the cell edit template 
        reader.onload =() => { 
            console.log(reader.result); 
            readString = reader.result; 
          }; 
          reader.onerror =(error: any) => { 
            console.log('Error: ', error); 
          }; 
    } 
     
}; 
 
    const editTemplate = (props: any) => { 
        return ( 
            <div> 
          <UploaderComponent id="uploader" 
          autoUpload={true} 
          multiple={false} 
          asyncSettings={uploadPath} 
          allowedExtensions='.png' 
          success={onUploadSuccess} /> 
           <p>{iconFileName}</p> 
          </div> 
        ); 
      }; 
 
    const gridRef: any = useRef(null) 
    const [iconFileName, setIconFileName] = useState('Initial value'); 
    const toolbarOptions: ToolbarItems[] = ["Add", "Edit", "Delete", "Cancel", "Search"]; 
    const editOptions: EditSettingsModel = { 
        allowEditing: true, 
        allowAdding: true, 
        allowDeleting: true, 
        mode: "Normal" 
      }; 
 
    const pageSettings:PageSettingsModel =  { pageSize: 6 }; 
    return(<div> <p>Grid-1 using Functional Components</p> 
    <GridComponent dataSource={employeeData} ref={gridRef} allowPaging={true} pageSettings={ pageSettings } 
             editSettings={ editOptions } actionBegin={begin} toolbar={toolbarOptions} allowPdfExport={true} allowExcelExport={true} allowFiltering={true}> 
            <ColumnsDirective> 
                <ColumnDirective field='OrderID' isPrimaryKey={true} width='100' textAlign="Left"/> 
                <ColumnDirective field='CustomerID' width='100' textAlign="Left"/> 
                <ColumnDirective field='CustomerName' width='100' textAlign="Left"/> 
                <ColumnDirective field='Image' template={template} editTemplate={editTemplate} width='100' textAlign="Left"/> 
            </ColumnsDirective> 
            <Inject services={[Page, PdfExport, Filter, Group, Edit, ExcelExport, Search, Toolbar]} /> 
        </GridComponent>  
        </div> 
) 
    } 
 


Regards, 
Rajapandi R


SP Spyros May 20, 2021 08:03 AM UTC

Hey there,

Thank you very much for the reply and provided solution. This indeed does work, but seems like a workaround to me, if I understand correctly instead of editing the state you change the DOM directly in order to not have the Grid react to state change and trigger a refresh.

This brings me to the second part of my original Query, that Grid was refreshing during every state change.
I have made a new thread discussing the issue in detail and have included an example app that demonstrates the issue.

The way this relates to this thread is that your solution does work for changing text via direct DOM edits, but does not help with rendering components or calling functions that make changes to the React state without having the Grid refresh itself.

I hope you can help me with this.
Thank you in advance.

Best regards!


RR Rajapandi Ravi Syncfusion Team May 21, 2021 11:45 AM UTC

Hi Spyros, 

Greetings from syncfusion support 

You can achieve your requirement by using memo. React.memo() is similar to PureComponent in that it will help us control when our components rerender. Please refer the below code example and sample for more information. 

Home.tsx 
 
import { ColumnDirective, ColumnsDirective, GridComponent } from '@syncfusion/ej2-react-grids'; 
import { Edit, EditSettingsModel, ExcelExport, Filter, Group, Inject, Page,PageSettingsModel, PdfExport, Search, Toolbar, ToolbarItems} from '@syncfusion/ej2-react-grids'; 
import React, {useRef } from 'react'; 
import { employeeData } from './datasource'; 
import ComponentA from './Component1'; 
import { readString } from './Component1'; 
 
const template = (props: any) => { 
    const src = props.Image; 
  return (<div style={{ height: "55px", width: "55px"}}> 
      <img src={src} alt={props.Image} /> 
  </div>); 
}; 
 
 
function Home() { 
  const begin = (props: any) => { 
    if (props.requestType === "save") { 
      props.data.Image = readString;     
    } 
    const editTemplate = (props: any) => { 
        return ( 
          <ComponentA /> 
        ); 
      }; 
 
    const gridRef: any = useRef(null) 
    const toolbarOptions: ToolbarItems[] = ["Add", "Edit", "Delete", "Cancel", "Search"]; 
    const editOptions: EditSettingsModel = { 
        allowEditing: true, 
        allowAdding: true, 
        allowDeleting: true, 
        mode: "Normal" 
      }; 
 
    const pageSettings:PageSettingsModel =  { pageSize: 6 }; 
    return(<div> <p>Grid-1 using Functional Components</p> 
    <GridComponent dataSource={employeeData} ref={gridRef} allowPaging={true} pageSettings={ pageSettings } 
             editSettings={ editOptions } actionBegin={begin} toolbar={toolbarOptions} allowPdfExport={true} allowExcelExport={true} allowFiltering={true}> 
            <ColumnsDirective> 
                <ColumnDirective field='OrderID' isPrimaryKey={true} width='100' textAlign="Left"/> 
                <ColumnDirective field='CustomerID' width='100' textAlign="Left"/> 
                <ColumnDirective field='CustomerName' width='100' textAlign="Left"/> 
                <ColumnDirective field='Image' template={template} editTemplate={editTemplate} width='100' textAlign="Left"/> 
            </ColumnsDirective> 
            <Inject services={[Page, PdfExport, Filter, Group, Edit, ExcelExport, Search, Toolbar]} /> 
        </GridComponent>  
        </div> 
    } 
 
export default Home; 
 

Component1.tsx 
 
import React, { memo, useState } from 'react'; 
import { UploaderComponent } from '@syncfusion/ej2-react-inputs'; 
 
export let readString: any = ""; 
 
const ComponentA = () => { 
    const [iconFileName, setIconFileName] = useState('Initial value'); 
 
    const uploadPath: object = { 
        removeUrl: 'https://aspnetmvc.syncfusion.com/services/api/uploadbox/Remove', 
        saveUrl: 'https://aspnetmvc.syncfusion.com/services/api/uploadbox/Save' 
    } 
    const onUploadSuccess = (props: any) => { 
        setIconFileName(props.file.name); 
      if(props.operation === 'upload') { 
          // Raw file of the uploaded image is retrieved 
          const file = props.file.rawFile; 
          // File reader is initialized and the raw file is read as URL 
          const reader = new FileReader(); 
          reader.readAsDataURL(file); 
          // The base64 result is retrieved on load success 
          // This value is stored in global variable and returned in the read function of the cell edit template 
          reader.onload =() => { 
              console.log(reader.result); 
              readString = reader.result; 
            }; 
            reader.onerror =(error: any) => { 
              console.log('Error: ', error); 
            }; 
        } 
    }; 
    return( 
      <div> 
          <UploaderComponent id="uploader" 
          autoUpload={true} 
          multiple={false} 
          asyncSettings={uploadPath} 
          allowedExtensions='.png' 
          success={onUploadSuccess} /> 
          <p>{iconFileName}</p> 
      </div> 
    ) 
 
export default memo(ComponentA); //it helps to prevent the Grid from refresh 


Regards,
Rajapandi R 


Marked as answer

SP Spyros May 27, 2021 12:51 PM UTC

Thank you for your reply. I have not yet had the chance to implement it to my application, but the provided sample application works great!
Have a great day!

Best regards,
Spyros


RR Rajapandi Ravi Syncfusion Team May 28, 2021 06:10 AM UTC

Hi Spyros, 

We are happy to hear that the provided solution is working fine at your end.  
 
Please get back to us if you need further assistance. 

Regards, 
Rajapandi R 



AA Adeel Ahmad Rana replied to Rajapandi Ravi October 15, 2021 06:48 AM UTC

Hi , I Am Facing the Same issue

when My SyncFusion . if my row is in edit mode and i click else where (Say Cancel Button ), My App crashes in console it gives the following error,Notice that I Am using To many custom Component.


Following is code.


import React from 'react'
import ReactDOM from 'react-dom'
import { Row, Col, Card,Spinner } from 'react-bootstrap'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import { TextField, Popper, Typography, FormControlLabel, Checkbox } from '@material-ui/core'
import { Save, Cancel, Print, PostAdd } from '@material-ui/icons'
import { enableRipple } from '@syncfusion/ej2-base';
import { Autocomplete } from '@material-ui/lab';
import { GridComponent, ColumnsDirective, ColumnDirective, Page, Filter, Inject, Edit, Sort, ForeignKey, Toolbar } from '@syncfusion/ej2-react-grids';
import Swal from 'sweetalert2'
import { getValue } from '@syncfusion/ej2-base';
import { NumericTextBoxComponent } from '@syncfusion/ej2-react-inputs';

import { SelectItemsListCombo, SelectItem, SelectItemMeasuring } from "../../Services/ItemsAPI"
import { SelectGodownsList } from '../../Services/GodownsAPI'
import { DepartmentList } from '../../Services/DepartmentsAPI'
import { MachinesList } from '../../Services/MachinesAPI'
import { GetMaxInventoryNo, SaveInvoice, SelectInvoiceMain, SelectInvoiceDetail, InvoicePost } from './../../Services/InvoicesAPI'
import { GetCurrentDate } from "../../Services/Domain";
import { ItemStock } from './../../Services/InventoryAPI'
import StockDetail from './../SaleInvoices/StockDetail'
import Tick from './../../Images/Tick.jpg'
import Cross from './../../Images/Cross.jpg';
import ProductionIssuanceModal from "../ProductionIssuance/ProductionIssuanceModal"

export class StockEntry extends React.Component {
constructor(props) {
super(props)

this.state = {
MainData: [],
StockData: [],
DeletedStockData: [],
ItemList: [],
itemData: [],
SelectedItem: null,
Selected_Godown_Err: null,
Selected_Deptt_Err: null,
Selected_Machine_Err: null,
SelectedTrType: null,
TransactionNo: '',
Qty: 0,
IsCancel: false,
GodownsListData: [],
GridGodownsListData: [],
DepttListData: [],
MachineListData: [],
SelectedGodownsList: null,
ItemMeasuringData: [],
ItemStockDetail: [],
SelectedItemMeasuringData: null,
isvisible: false,
InvoiceDate: GetCurrentDate(),
IsReLocation: false,
IsAdjustment: false,
StockDetailModal: false,
IsIssuance: this.props.StockTitle == "Stock Issuance" ? true : false,
UserName: '',
isUpdate: true,
GodownErr: '',
TrTypeErr: '',
post: '',
btnSave: true,
btnPost: true,
ProductionIssuance: false,
ProductionIssuanceTitle: "",
IsHidden_ProductionIssuance: true,
btn_Disabled: false
}
this.toolbarOptions = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
this.validationRules = { required: true };
this.ItemDescription_Ref = React.createRef()
this.customItemValidation = this.customItemValidation.bind(this)
this.customGodownValidation = this.customGodownValidation.bind(this)
this.customDepttValidation = this.customDepttValidation.bind(this)
this.customMachineValidation = this.customMachineValidation.bind(this)
this.grid = this.grid
this.Comp_Id = this.props.CompID.Branch_Id
this.Project_ID = this.props.Project && this.props.Project.Project_ID
this.ProjectName = this.props.ProjectName && this.props.ProjectName
this.ItemValidation = {
required: [this.customItemValidation, 'Item must be selected']
}
this.GodownValidation = {
required: [this.customGodownValidation, 'Godown must be selected']
}
this.DepttValidation = {
required: [this.customDepttValidation, 'Deptt must be selected']
}
this.MachineValidation = {
required: [this.customMachineValidation, 'Machine must be selected']
}
}

show_Production_Issuance_Modal = () => {
this.setState({ ProductionIssuance: true })
}

hide_Production_Issuance_Modal = () => {
this.setState({ ProductionIssuance: false })
}

customItemValidation(args) {
args.value = this.state.SelectedItem ? this.state.SelectedItem.ItemID : ''
return getValue('value', args) != ''
}
customGodownValidation(args) {
args.value = this.state.Selected_Godown_Err ? this.state.Selected_Godown_Err.God_ID : ''
return getValue('value', args) != ''
}
customDepttValidation(args) {
args.value = this.state.Selected_Deptt_Err ? this.state.Selected_Deptt_Err.DeptID : ''
return getValue('value', args) != ''
}
customMachineValidation(args) {
args.value = this.state.Selected_Machine_Err ? this.state.Selected_Machine_Err.MachineID : ''
return getValue('value', args) != ''
}
sortingOptions = {
columns: [{ field: 'sNo', direction: 'Ascending' }]
};

componentDidUpdate() {
if (this.state.isUpdate) {
this.setState({ isUpdate: false })
}
}
componentDidMount() {
SelectItemsListCombo(this.Comp_Id, (data) => {
this.setState({ ItemList: data })
})
SelectGodownsList(this.Comp_Id, (data) => {
this.setState({ GodownsListData: data, GridGodownsListData: data })
})
DepartmentList(this.Comp_Id, (data) => {
this.setState({ DepttListData: data })
})
MachinesList(this.Comp_Id, (data) => {
this.setState({ MachineListData: data })
})
})
})
}

if (this.props.StockTitle == "Stock Issuance") {
this.setState({ IsReLocation: true, IsAdjustment: true })
}
else if (this.props.StockTitle == "Stock Adjustment") {
this.setState({ IsReLocation: true, IsAdjustment: false })
}
else {
this.setState({ IsReLocation: false, IsAdjustment: true })
}
this.setState({btn_Disabled: false})
}
Select_Item = (ItemID, callback) => {
SelectItem(this.Comp_Id, ItemID, (data) => {
callback(data)
})
}
Select_Item_Measuring = (ItemID, callback) => {
SelectItemMeasuring(this.Comp_Id, ItemID, (data) => {
callback(data)
})
}


ItemDescription_Field = (rowValue) => {
return (
<Autocomplete
name="ItemDescription"
id="itemDescription"
defaultValue={this.state.ItemList.find(v => v.ItemDesc === rowValue.ItemDescription)}
ref={this.ItemDescription_Ref}
options={this.state.ItemList}
getOptionLabel={(option) => option.ItemDesc ? option.ItemDesc : ""}
onChange={(e, value) => {

if (value) {
this.Item_And_ItemMeasuring(value.ItemID)
this.setState({ SelectedItem: value })
}
else {

var munit_select = document.getElementById('Munit')

var length = munit_select.options.length;
for (let i = length - 1; i >= 0; i--) {
munit_select.options[i] = null;
}
this.setState({ SelectedItem: null })
}
}}

renderInput={(params) =>
<TextField {...params} name="ItemDescription" />}
/>
)
}
Item_And_ItemMeasuring = (ItemID) => {
this.Select_Item(ItemID, (data) => {

this.Select_Item_Measuring(ItemID, (itemMeasuringData) => {

itemMeasuringData[0].BasicUnit_Qty = data[0].Qty_WH
this.setState({ itemData: data, ItemMeasuringData: itemMeasuringData, SelectedItemMeasuringData: itemMeasuringData[0] })

var munit_select = document.getElementById('Munit')

var length = munit_select.options.length;
for (let i = length - 1; i >= 0; i--) {
munit_select.options[i] = null;
}
this.setState({ ItemMeasuringData: itemMeasuringData })
for (let i = 0; i < itemMeasuringData.length; i++) {
munit_select.options[munit_select.options.length] = new Option(itemMeasuringData[i].MUnitDesc, itemMeasuringData[i].MUnitID)
}
munit_select.selectedIndex = 0


})
})
}
Qty = (rowValue) => {
return (
<NumericTextBoxComponent
id="Qty"
name="Qty"
decimals={2}
format={"n2"}
min={0}
showSpinButton={false}
ref={(numeric) => { this.numericInstance = numeric }}
value={typeof rowValue.Qty === "string" ? (rowValue.Qty).replace(/,/g, '') : rowValue.Qty}
required={true}
/>
)
}
Godowns = (rowValue) => {
this.godown_object = <Autocomplete
name="Godown"
id="Godowns"
defaultValue={this.state.GridGodownsListData.find(x => x.GodDesc === rowValue.Godown)}
options={this.state.GridGodownsListData}
getOptionLabel={(option) => option.GodDesc ? option.GodDesc : ""}
onChange={(e, value) => {
if (value) {
this.setState({ Selected_Godown_Err: value })
}
else {
this.setState({ Selected_Godown_Err: null })
}
}}
renderInput={(params) =>
<TextField {...params} name="Godown"
/>}
/>
return (this.godown_object)
}
Deptt = (rowValue) => {
this.Deptt_object = <Autocomplete
name="DeptName"
id="DeptName"
defaultValue={this.state.DepttListData.find(x => x.DeptName === rowValue.DeptName)}
options={this.state.DepttListData}
getOptionLabel={(option) => option.DeptName ? option.DeptName : ""}
getOptionSelected={(option, value) => option.DeptID === value.DeptID}
onChange={(e, value) => {
if (value) {
this.setState({ Selected_Deptt_Err: value })
}
else {
this.setState({ Selected_Deptt_Err: null })
}
}}
renderInput={(params) =>
<TextField {...params} name="DeptName"
/>
}
/>
return (this.Deptt_object)
}
Machines = (rowValue) => {
this.Machine_object = <Autocomplete
name="MachineName"
id="MachineName"
defaultValue={this.state.MachineListData.find(x => x.MachineName === rowValue.MachineName)}
options={this.state.MachineListData}
getOptionLabel={(option) => option.MachineName ? option.MachineName : ""}
getOptionSelected={(option, value) => option.MachineID === value.MachineID}
onChange={(e, value) => {
if (value) {
this.setState({ Selected_Machine_Err: value })
}
else {
this.setState({ Selected_Machine_Err: null })
}
}}
renderInput={(params) =>
<TextField {...params} name="MachineName"
/>}
/>
return (this.Machine_object)
}
M_Unit = (rowValue) => {
return (<select id="Munit" name="MUnit"
style={{ fontSize: 17, textAlign: "right", width: "100%" }}
onChange={e => {
var Qty = 0;
this.state.ItemMeasuringData.find(value => {

if (e.target.value == value.MUnitID) {
this.setState({ SelectedItemMeasuringData: value })
}
})
}}
className="textbox" >select>)

}
beforeRowInsert = (rowValue) => {
if (rowValue.rowData) {
if (rowValue.data) {
if (rowValue.data.ItemDescription) {

if (rowValue.requestType != "cancel") {
var select = document.getElementById('Munit')
var name = select.options[select.selectedIndex].text
var value = select.options[select.selectedIndex].value
rowValue.data.MUnit = name
rowValue.data.MUnitID = value

this.state.GridGodownsListData.find(value => {
if (value.GodDesc === rowValue.data.Godown) {
rowValue.data.God_ID = value.God_ID
}
})

this.state.ItemList.find(value => {
if (value.ItemDesc === rowValue.data.ItemDescription) {
rowValue.data.ItemID = value.ItemID
}
})

this.state.DepttListData.find(value => {
if (value.DeptName === rowValue.data.DeptName) {
rowValue.data.DeptID = value.DeptID
}
})
this.state.MachineListData.find(value => {
if (value.MachineName === rowValue.data.MachineName) {
rowValue.data.MachineID = value.MachineID
}
})
rowValue.data.Amount = 0
rowValue.data.BasicUnit_Qty = 0
rowValue.data.Qty_Out = 0
rowValue.data.Rate = 0
rowValue.data.Tr_Type = 'AJV'
rowValue.data.action = rowValue.action
if (rowValue.data.InvDID === undefined) {
rowValue.data.action = 'add'
}
}
}
else {
rowValue.data.sNo = this.state.StockData.length + 1
rowValue.data.Qty = 0.00
this.setState({ SelectedItem: null, Selected_Godown_Err: null, Selected_Deptt_Err: null, Selected_Machine_Err: null })
}
}
}

}
afterRowInsert = (rowValue) => {

if (rowValue.requestType == "beginEdit") {
this.Select_Item(rowValue.rowData.ItemID, (data) => {
this.Select_Item_Measuring(rowValue.rowData.ItemID, (itemMeasuringData) => {

var munit_select = document.getElementById('Munit')
var length = munit_select.options.length;
for (let i = length - 1; i >= 0; i--) {
munit_select.options[i] = null;
}
for (let i = 0; i < itemMeasuringData.length; i++) {
if (rowValue.rowData.MUnit == itemMeasuringData[i].MUnitDesc) {
itemMeasuringData[i].BasicUnit_Qty = data[0].Qty_WH
munit_select.options[munit_select.options.length] = new Option(itemMeasuringData[i].MUnitDesc, itemMeasuringData[i].MUnitID)
munit_select.selectedIndex = i
this.setState({ ItemMeasuringData: itemMeasuringData, SelectedItemMeasuringData: itemMeasuringData[i], itemData: data })

}
else {
munit_select.options[munit_select.options.length] = new Option(itemMeasuringData[i].MUnitDesc, itemMeasuringData[i].MUnitID)
}
}
})
})
this.state.ItemList.find(value => {
if (value.ItemID === rowValue.rowData.ItemID) {
this.setState({ SelectedItem: value })
}
})
this.state.GridGodownsListData.map(value => {
if (value.God_ID === rowValue.rowData.God_ID) {
this.setState({ Selected_Godown_Err: value })
}
})
this.state.DepttListData.find(value => {
if (value.DeptID === rowValue.rowData.DeptID) {
this.setState({ Selected_Deptt_Err: value })
}
})
this.state.MachineListData.find(value => {
if (value.MachineID === rowValue.rowData.MachineID) {
this.setState({ Selected_Machine_Err: value })
}
})
}
else if (rowValue.requestType == "delete") {
if (rowValue.data[0].InvDID) {
this.setState({ DeletedStockData: this.state.DeletedStockData.concat(rowValue.data) })
}
}
else if (rowValue.requestType == "save") {

}
else if (rowValue.requestType == "add") {
document.getElementById('itemDescription').focus()
}
}

render() {
if (this.grid)
this.grid.autoFitColumns(['sNo', 'ItemDescription', 'Qty', 'Godown', 'Mnuit'])
enableRipple(true);
return ReactDOM.createPortal(
<>
<div className="block-header">
<Row>
<Col lg={7} md={6} sm={12}>
<div className="header_Title" >
{this.props.StockTitle} / {this.ProjectName}
div>
Col>
Row>
<Card>
<Row className=" row-margin-left row-margin-right row-margin-top" >
  <Col >
                                {this.state.btnPost && <button id="btn_Post" type="button" className="btn btn-primary waves-effect height-of-button" onClick={this.Invoice_Post} > <PostAdd /> {this.state.post} </button>}
                                {this.state.btnSave && <button type="button" className="btn btn-primary waves-effect height-of-button" onClick={this.Save_Stock} disabled={this.state.btn_Disabled}>
                                    {this.state.btn_Disabled ? (<><Spinner
                                        as="span"
                                        animation="grow"
                                        size="sm"
                                        role="status"
                                        aria-hidden="true"
                                    /> Saving... </>) : <> <Save /> Save </>}</button>}
                                <Link to="/Stock" className="btn btn-primary waves-effect height-of-button" id="btnCancel"> <Cancel /> Cancel</Link>
                            </Col>
                        </Row>
<Row className=" row-margin-left row-margin-right row-margin-top" >
<div style={{ display: "flex", width: "100%", height: "100%", overflow: "hidden" }}>
<div style={{ flex: "1", width: "100%", height: "100%" }}>
<GridComponent dataSource={this.state.StockData} actionBegin={this.beforeRowInsert} ref={g => this.grid = g} actionComplete={this.afterRowInsert} allowPaging={true} allowFiltering={true} allowSorting={true} editSettings={{ allowEditing: true, allowDeleting: true, allowAdding: true, newRowPosition: 'Bottom' }} sortSettings={this.sortingOptions} filterSettings={{ type: 'Menu' }} toolbar={this.toolbarOptions}>
<ColumnsDirective>
<ColumnDirective width='70' field='sNo' headerText='SrNo #' Format="{0:n}" allowEditing={false} isIdentity={true} textAlign='Left' isPrimaryKey={true}>ColumnDirective>
<ColumnDirective width='250' field='ItemDescription' headerText='Item Description' editType='dropdownedit' editTemplate={this.ItemDescription_Field} validationRules={this.ItemValidation} textAlign='Left' />
<ColumnDirective field='Stock' headerText='Stock' width='80' editTemplate={rowData => (<Link to="#" name="Stock" id="Stock" onClick={e => {
if (this.state.SelectedItem !== null) {
this.showStockDetailModal(this.state.SelectedItem.ItemID)
}
else {
Swal.fire({
icon: 'error',
text: `Please Select Item`,
})
}
}}
> Stock Link>)} textAlign="center" >ColumnDirective>
<ColumnDirective width='170' field='Qty' headerText='Qty' editTemplate={this.Qty} textAlign='right' />
<ColumnDirective width='130' field='MUnit' headerText='M Unit' editTemplate={this.M_Unit} />
<ColumnDirective width='250' field='Godown' headerText='Godown' editType='dropdownedit' editTemplate={this.Godowns} validationRules={this.GodownValidation} >ColumnDirective>
<ColumnDirective field='DeptName' headerText='Department' editType='dropdownedit' visible={this.state.IsIssuance} editTemplate={this.Deptt} validationRules={this.DepttValidation} >ColumnDirective>
<ColumnDirective field='MachineName' headerText='Machine' editType='dropdownedit' visible={this.state.IsIssuance} editTemplate={this.Machines} validationRules={this.MachineValidation} >ColumnDirective>
<ColumnDirective field='OrderNo' headerText='Order No' allowEditing={false} >ColumnDirective>
<ColumnDirective field='ItemID' headerText='ItemID' visible={false}>ColumnDirective>
<ColumnDirective field='God_ID' headerText='God_ID' width='150' hideAtMedia={true} >ColumnDirective>
<ColumnDirective field='MUnitID' width='150' hideAtMedia={true} >ColumnDirective>
<ColumnDirective field='InvDID' hideAtMedia={true} >ColumnDirective>
<ColumnDirective field='BasicUnit_Qty' hideAtMedia={true} >ColumnDirective>
<ColumnDirective field='Amount' hideAtMedia={true} >ColumnDirective>
<ColumnDirective field='OrderID' hideAtMedia={true} >ColumnDirective>
ColumnsDirective>
<Inject services={[Filter, Page, Edit, Sort, ForeignKey, Toolbar]} />
GridComponent>
div>
div>
<StockDetail
show={this.state.StockDetailModal}
hide={this.hideStockDetailModal}
itemstockdata={this.state.ItemStockDetail}
/>
Row>
<Row className="row-margin-left row-margin-top row-margin-right" >
<Col lg={2} xl={2} md={4} id="IsCancel">
<FormControlLabel
control={
<Checkbox
checked={this.state.IsCancel}
name="checkedB"
color="primary"
onChange={e => this.setState({ IsCancel: e.target.checked })}
/>
}
label="Cancel"
/>
Col>
<Col lg={2} xl={2} md={4} style={{ marginTop: "1%" }} id="Posted">
<p>Posted : <img id="img" height='20' width='20' />p>
Col>
Row>

<ProductionIssuanceModal
show={this.state.ProductionIssuance}
onHide={this.hide_Production_Issuance_Modal}
project_Id={this.props.Project && this.props.Project.Project_ID}
title={this.state.ProductionIssuanceTitle}
save={this.Save_Production}
Dispatch={false}
invType={"S"}
/>
Card>
div>
, document.getElementById('mainContent')
)
}
}

const mapStateToProps = state => ({
})
export default connect(mapStateToProps)(StockEntry)


Attachment: Console_a767da5b.rar



RS Rajapandiyan Settu Syncfusion Team October 18, 2021 12:11 PM UTC

Hi Adeel, 

Thanks for contacting Syncfusion support. 

Currently, we are preparing the sample as per your code example and we need some time on this. So, we will update the further details on or before Oct 20th 2021. 

We appreciate your patience until then. 

Regards, 
Rajapandiyan S 



RR Rajapandi Ravi Syncfusion Team October 19, 2021 10:46 AM UTC

Hi Adeel, 

Thanks for your patience 

Before we start providing solution on your query, we need some information for our clarification. Please share the below details that will be helpful for us to provide better solution. 

1)          Please share your syncfusion package version. 

2)          Please share the issue replication procedure step by step. 

3)          Please share the issue scenario in video demonstration format. 

4)          If possible, please share any issue reproducing sample. 

Rajapandi R 



AA Adeel Ahmad Rana replied to Rajapandi Ravi October 22, 2021 05:49 AM UTC

Hi Ravi,

sorry for replaying late , here is the answers of your Queries

1) Please share your syncfusion package version.

"@syncfusion/ej2-base": "^19.1.56",
"@syncfusion/ej2-react-grids": "^19.1.56",
"@syncfusion/ej2-react-inputs": "^19.1.54",
"@testing-library/jest-dom": "^5.11.9",


2) Please share the issue replication procedure step by step.

i) Here Is my row (displaying single row) having custom validation Complete Code is given above Question

<ColumnDirective width='250' field='ItemDescription' headerText='Item Description' editType='dropdownedit' editTemplate={this.ItemDescription_Field} validationRules={this.ItemValidation} textAlign='Left' />


ii) Here is my Custom Validation

a)Constructor

this.customItemValidation = this.customItemValidation.bind(this)
this.ItemValidation = {
required: [this.customItemValidation, 'Item must be selected']
}
/// Custom validation
customItemValidation(args) {
args.value = this.state.SelectedItem ? this.state.SelectedItem.ItemID : '
return getValue('value', args) != ''
}


iii) Custom control being rendered by Grid

ItemDescription_Field = (rowValue) => {
return (
<Autocomplete
name="ItemDescription"
id="itemDescription"
defaultValue={this.state.ItemList.find(v => v.ItemDesc === rowValue.ItemDescription)}
ref={this.ItemDescription_Ref}
options={this.state.ItemList}
getOptionLabel={(option) => option.ItemDesc ? option.ItemDesc : ""}
onChange={(e, value) => {

if (value) {
}
else {

}
}}

renderInput={(params) =>
<TextField {...params} name="ItemDescription" />}
/>
)
}


and this is my cancel button ,

<Link to="/Stock" className="btn btn-primary waves-effect height-of-button" id="btnCancel"> <Cancel /> CancelLink>


if my grid is edit mode (Say one row is edited state ) before closing this row(for example without pressing cancel or update button from toolbar of grid) .

I click on this cancel button (on form),which is navigating to another page the app crashes.

Note : this cancel button is present on form , this is not the syncfusion's toolbar Cancel button .

I am attaching the error picture .


any help would be highly appreciated.

Thank You.


Attachment: Console_a767da5b_140eda72.rar



AA Adeel Ahmad Rana October 22, 2021 06:02 AM UTC

Here is Video Demontration.


Attachment: video_f2634bb7.rar


RR Rajapandi Ravi Syncfusion Team October 25, 2021 01:29 PM UTC

Hi Adeel, 

Thanks for the update 

We have checked your shared console error call stack and we could see that the console error was comes from your component and it not comes from the Syncfusion Grid. So, to check your reported problem we need your problem reproducing sample. So please send your issue reproducing sample that will be helpful for us to validate.  

Rajapandi R 



Loader.
Up arrow icon