I have a page that looks like this:
When clicking the row's edit button, the component is supposed to set the selected entity to that row and put itself into "EDIT" mode.
However, when displaying a box in the upper right (component called toast here), the mode is evaluated to null
on the very first click:
Here's the code:
import React, { Component } from 'react';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Button } from 'primereact/button';
import { Toast } from 'primereact/toast';
import 'primereact/resources/themes/luna-blue/theme.css';
import 'primereact/resources/primereact.min.css';
import 'primeicons/primeicons.css';
const modes =
{
VIEW: 'VIEW',
ADD: 'ADD',
EDIT: 'EDIT',
REMOVE: 'REMOVE'
}
class TestManager extends Component
{
constructor()
{
super();
let persons = [
{
"id": 3,
"zipCode": "",
"cityName": "",
"streetName": "",
"houseNbr": "",
"firstName": "Ahmed",
"lastName": "Al-Thiab",
"gender": "MALE",
},
{
"id": 4,
"zipCode": "",
"cityName": "",
"streetName": "",
"houseNbr": "",
"firstName": "Stefan",
"lastName": "Antalovics",
"gender": "MALE",
},
{
"id": 20,
"zipCode": "",
"cityName": "",
"streetName": "",
"houseNbr": "",
"firstName": "Christian",
"lastName": "Attina",
"gender": "MALE",
},
{
"id": 15,
"zipCode": "",
"cityName": "",
"streetName": "",
"houseNbr": "",
"firstName": "Filmon",
"lastName": "Berhane",
"gender": "MALE",
},
{
"id": 2,
"zipCode": "64895",
"cityName": "Darmstadt",
"streetName": "",
"houseNbr": "",
"firstName": "Johannes",
"lastName": "Loczewski",
"gender": "MALE",
},
{
"id": 1,
"zipCode": "22880",
"cityName": "Wedel",
"streetName": "Rosengarten",
"houseNbr": "6",
"firstName": "Karsten",
"lastName": "Wutzke",
"gender": "MALE",
}
]
this.state = {entities: persons,
selectedEntity: null,
mode: null};
this.onRowEdit = this.onRowEdit.bind(this);
this.onRowRemove = this.onRowRemove.bind(this);
this.actions = this.actions.bind(this);
}
render()
{
var header = "Person Manager (" + this.state.entities.length + ")"
return (
<div style={{ maxWidth: 1000, marginLeft: "auto", marginRight: "auto", marginTop: 10, marginBottom: 10 }}>
<Toast ref={(el) => this.toast = el} />
<DataTable value={this.state.entities}
header={header}
dataKey="id"
selection={this.state.selectedEntity}
selectionMode="single"
sortField='lastName'
sortOrder={1}
resizableColumns
columnResizeMode="fit"
className="p-datatable-striped">
<Column field="id" header='ID' sortable style={{width:'7.5%'}} />
<Column field="gender" header='Sal.' body={this.salutation} sortable style={{width:'10%'}} />
<Column field='lastName' header='Last Name' sortable style={{width:'15%'}} />
<Column field='firstName' header='First Name' sortable style={{width:'15%'}} />
<Column field='streetName' header='Street' body={this.street} sortable style={{width:'20%'}} />
<Column field='zipCode' header='ZIP' sortable style={{width:'10%'}} />
<Column field='cityName' header='City' sortable style={{width:'10%'}} />
<Column field="actions" body={this.actions} style={{width:'12.5%'}} />
</DataTable>
</div>
);
}
actions(rowData)
{
return (
<>
<div style={{textAlign: "center"}}>
<Button icon="pi pi-pencil"
tooltip="Edit"
onClick={() => this.onRowEdit(rowData)}
className="p-button-sm p-button-raised p-button-rounded p-button-outlined" />
<Button icon="pi pi-trash"
tooltip="Remove"
className="p-button-sm p-button-raised p-button-rounded p-button-outlined"
onClick={() => this.onRowRemove(rowData)}
style={{marginLeft: 5}} />
</div>
</>
);
}
onRowEdit(rowData)
{
this.setState({selectedEntity: rowData});
this.setState({mode: modes.EDIT});
console.log("Last name: " + rowData.lastName);
console.log("Mode: " + this.state.mode);
this.toast.show({ severity: 'info', summary: 'Editing Person', detail: 'Name: ' + rowData.lastName + ", " + this.state.mode, life: 3000 });
}
onRowRemove(rowData)
{
this.setState({selectedEntity: rowData});
this.setState({mode: modes.REMOVE});
this.toast.show({ severity: 'info', summary: 'Removing Person', detail: 'Name: ' + rowData.lastName + ", " + this.state.mode, life: 3000 });
}
salutation(rowData)
{
var gender = rowData['gender'];
if ( gender )
{
switch( gender )
{
case "MALE":
return "Mr";
case "FEMALE":
return "Mrs";
default:
return "Error";
}
}
return null;
}
street(rowData)
{
var streetName = rowData['streetName'];
var houseNumber = rowData['houseNbr'];
return houseNumber ? streetName + " " + houseNumber : streetName;
}
}
export default TestManager;
QUESTION:
What's wrong here?
According to this
Does React keep the order for state updates?
there shouldn't be any problems with the method callback, a.k.a. as event handler, updating the mode
onRowEdit(rowData)
{
this.setState({selectedEntity: rowData});
this.setState({mode: modes.EDIT});
console.log("Last name: " + rowData.lastName);
console.log("Mode: " + this.state.mode);
this.toast.show({ severity: 'info', summary: 'Editing Person', detail: 'Name: ' + rowData.lastName + ", " + this.state.mode, life: 3000 });
}
this.state.mode
is always null
on the very first click. On subsequent clicks, no matter which row, the correct mode is being shown.
Why?
How do I fix this?
BTW: this.state.selectedEntity
on the line using toast is null
as well.
I'm having trouble understanding this. -> self-learner
The state updates won't be (necessarily) reflected in
this.state
until the next render.From the docs:
You already know the mode, so you can either pass
modes.EDIT
instead ofthis.state.mode
:or use the callback form of setState which ensures that state has updated before calling the subsequent toast code:
Note: it's possible that your
console.log
call will show the updated value because console.log runs asynchronously. (Between the time you call it and the time it appears in the console the state may have changed.)