How to trigger componentdidmount on click react - reactjs

I have an application to display at a table. I am fetching api from django rest framework, the API is paginated. So when I load the react app it loads the first page( for example it callshttp://localhost:8000/cluster/37/tasks?page=1) by default. I have a next button.I am trying to go to the next page(for example it should call http://localhost:8000/cluster/37/tasks?page=2) on clicking next.
How do I attempt to trigger the fetch on clicking the next button? Thanks.
Here is sample of my code below :
class TasksApp extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 1,
columnDefs: [
{ headerName: 'Temp RowNumber', valueGetter: 'node.rowIndex'},
{ headerName: 'Status', field: 'status' },
{ headerName: 'Params', field: 'joint_params' },
{ headerName: 'Total Pages', field: 'total_pages' },
{ headerName: 'Total Results', field: 'total_results' },
],
defaultColDef: { resizable: true },
rowData: null,
dataLength: 0,
id: this.props.location.state.id,
task: this.props.match.params.value,
headerHeight: 39,
rowHeight: 49,
paginationPageSize: 200,
totalPages: null,
currentPage: null,
pageSize: null,
pageNumberList: [],
pageSizeList: [],
startIndex: 0,
endIndex: 5,
};
}
onGridReady = params => {
this.gridApi = params.api;
};
componentDidMount(){
fetch(`http://localhost:8000/cluster/${this.state.id}/tasks?page=${this.state.page}`, options)
.then(res => res.json())
.then(data => this.setState({
rowData: data['results'],
dataLength: data['totalDataOnPage'],
totalData: data['totalData'],
currentPage: data['currentPage'],
totalPages: data['totalPages'],
nextLink: data['nextPage'],
previousLink: data['previousPage']
}))
.catch(err => console.log(err))
}
onPaginationChanged = () => {
let list_pages = []
if (this.gridApi) {
console.log('total pages', this.state.totalPages)
for (var i = 1; i <= this.state.totalPages; i++) {
list_pages.push(i);
}
this.setState({ pageNumberList: list_pages })
}
};
onBtNext = () => {
//how do I trigger the componentDidMount to load the next page number on click next
var url = new URL(this.state.nextLink);
var pagenumber = url.searchParams.get("page");
this.setState({ page: pagenumber })
}
render() {
const pagelist = this.state.pageNumberList.slice(this.state.startIndex, this.state.endIndex)
return (
<>
//code truncated to show the `next`, the table and another part of pagination was here.
.......
next button below..
{!pagelist.includes(this.state.totalPages) ?
<PageDirection onClick={() => this.onBtNext()} style={{marginLeft: '15px'}}>
<ChevronRight style={{ padding: '5px' }} />
</PageDirection>
: null
}
</PaginationSectorTwo>
</div>
</Fragment>
</TableContent>
</InnerWrapper>
</Wrapper>
</>
)
}
}
export default TasksApp;

You shouldn't try to trigger componentDidMount on click because componentDidMount is executed only once in the lifecycle of the component, immediately after a component is mounted.
You should change onBtNext() function to fetch the data.

Cant you just add an onClick event on the button to fetch again?
<button onClick={() => call your api method here}><button/>

Related

calling setState from onClick JavaScript function not working

I am trying to create a button that will make visible a form to edit any contact on my list. However, when I press the button, nothing happens.
I have the initial state set to
this.state = {
contacts: [],
showEditWindow: false,
EditContactId: ''
};
I added a function:
editContact = (id) => {
this.setState({
showEditWindow: true, EditContactId: {id}
});
};
and a column:
{
title: "",
key: "action",
render: (record) => (
<button onClick={() => this.editContact(record.id)}
>
Edit
</button>
)
},
I imported EditContactModal and call it as
<EditContactModal reloadContacts={this.reloadContacts}
showEditWindow={this.state.showEditWindow}
EditContactId={this.state.EditContactId}/>
If I manually set this.state to showEditWindow:true, the window appears; however, either this.editContact(id) is not being called or it is not changing the state.
Calling this.deleteContact(id) works fine, as does setState in loadContacts() and reloadContacts()
What I am doing wrong?
Below are the full components.
Contacts.jsx
import { Table, message, Popconfirm } from "antd";
import React from "react";
import AddContactModal from "./AddContactModal";
import EditContactModal from "./EditContactModal";
class Contacts extends React.Component {
constructor(props) {
super(props);
this.state = {
contacts: [],
showEditWindow: false,
EditContactId: ''
};
this.editContact = this.editContact.bind(this);
};
columns = [
{
title: "First Name",
dataIndex: "firstname",
key: "firstname"
},
{
title: "Last Name",
dataIndex: "lastname",
key: "lastname"
},{
title: "Hebrew Name",
dataIndex: "hebrewname",
key: "hebrewname"
},{
title: "Kohen / Levi / Yisroel",
dataIndex: "kohenleviyisroel",
key: "kohenleviyisroel"
},{
title: "Frequent",
dataIndex: "frequent",
key: "frequent",
},{
title: "Do Not Bill",
dataIndex: "donotbill",
key: "donotbill"
},
{
title: "",
key: "action",
render: (record) => (
<button onClick={() => this.editContact(record.id)}
>
Edit
</button>
)
},
{
title: "",
key: "action",
render: (_text, record) => (
<Popconfirm
title="Are you sure you want to delete this contact?"
onConfirm={() => this.deleteContact(record.id)}
okText="Yes"
cancelText="No"
>
<a type="danger">
Delete{" "}
</a>
</Popconfirm>
),
},
];
componentDidMount = () => {
this.loadContacts();
}
loadContacts = () => {
const url = "http://localhost:3000/contacts";
fetch(url)
.then((data) => {
if (data.ok) {
return data.json();
}
throw new Error("Network error.");
})
.then((data) => {
data.forEach((contact) => {
const newEl = {
key: contact.id,
id: contact.id,
firstname: contact.firstname,
lastname: contact.lastname,
hebrewname: contact.hebrewname,
kohenleviyisroel: contact.kohenleviyisroel,
frequent: contact.frequent.toString(),
donotbill: contact.donotbill.toString(),
};
this.setState((prevState) => ({
contacts: [...prevState.contacts, newEl],
}));
});
})
.catch((err) => message.error("Error: " + err));
};
reloadContacts = () => {
this.setState({ contacts: [] });
this.loadContacts();
};
deleteContact = (id) => {
const url = `http://localhost:3000/contacts/${id}`;
fetch(url, {
method: "delete",
})
.then((data) => {
if (data.ok) {
this.reloadContacts();
return data.json();
}
throw new Error("Network error.");
})
.catch((err) => message.error("Error: " + err));
};
editContact = (id) => {
this.setState({
showEditWindow: true, EditContactId: {id}
});
};
render = () => {
return (
<>
<Table
className="table-striped-rows"
dataSource={this.state.contacts}
columns={this.columns}
pagination={{ pageSize: this.pageSize }}
/>
<AddContactModal reloadContacts={this.reloadContacts} />
<EditContactModal reloadContacts={this.reloadContacts}
showEditWindow={this.state.showEditWindow}
EditContactId={this.state.EditContactId}/>
</>
);
}
}
export default Contacts;
EditContactModal.jsx
import { Button, Form, Input, Modal, Select } from "antd";
import React from "react";
import ContactForm from './ContactForm';
const { Option } = Select;
class EditContactModal extends React.Component {
formRef = React.createRef();
state = {
visible: this.props.showEditWindow,
};
onFinish = (values) => {
const url = `http://localhost:3000/contacts/${this.props.EditContactId}`;
fetch(url, {
method: "put",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(values),
})
.then((data) => {
if(data.ok) {
this.handleCancel();
return data.json();
}
throw new Error("Network error.");
})
.then(() => {
this.props.reloadContacts();
})
.catch((err) => console.error("Error: " + err))
};
showModal = () => {
this.setState({
visible: true,
});
};
handleCancel = () => {
this.setState({
visible: false,
});
};
render() {
return (
<>
{/*<Button type="primary" onClick={this.showModal}>
Create New +
</Button>*/}
<Modal
title="Edit Contact"
visible={this.state.visible}
onCancel={this.handleCancel}
footer={null}
>
<ContactForm />
</Modal>
</>
);
}
}
export default EditContactModal;
if your aim is to perform an update to the state object, you must not pass mutable data, but copy it instead into a new object.
this will allow the state changes to be picked up.
so, prefer setState({ ...state, ...someObject }) over setState(someObject).

Unable to go to next page using the onChange function in MaterialTable

I'm trying to change the page in MaterialTable but am unable to do so. Although, the page size functionality is working but the page change functionality isn't.
Here is my state:
constructor(props) {
super(props)
this.state = {
status: false,
message: "",
page: 0,
pageSize: 5,
}
}
And inside MaterialTable, I have this:
<MaterialTable
title=""
page={this.state.page}
totalCount={this.props.operations.ids ? this.props.operations.ids.length : 0}
columns={[
{
title: 'Sr No.', field: 'serialNumber', render: rowData => {
return _.findIndex(renderingData, { "id": rowData.id }) + 1
}
},
{ title: 'Time Stamp', field: 'date', render: rowData => { return moment(rowData.date).format("YYYY/MM/DD hh:mm a") } },
{ title: 'Details', field: 'name' },
{
title: 'View Details', field: 'viewDetails', render: rowData => {
return <Button
variant="contained"
color={"primary"}
onClick={() => this.props.setTab(rowData)}
>View</Button>
}
},
]}
onChangePage={(page, pageSize) => {
this.setState({ ...this.state, page, pageSize})
}}
data={renderingData}
/>
Let me know if any modification is required for this. I still haven't been able to solve the problem.
clicking on next button , you also need to increase the page number
onChangePage={(event,page ) => { this.setState({ ...this.state, page}) }}
also change
function handleChangeRowsPerPage(event) { setRowsPerPage(+event.target.value); }
Ok, so the problem was that the data that goes into Material Table was the same even after clicking the Next page button. I went around it by adding this in the data. The rest of the code is the same.
data={renderingData.slice(this.state.page*this.state.pageSize, this.state.page*this.state.pageSize + this.state.pageSize)}
This code makes sure that new data is present when you click on next page.

How to trigger a fetch to pick next page on clicking next button react

I am building an application to display data from a django rest api. The data in django rest api is paginated using pagination_class. So I am trying to make the frontend pagination to be completely dependent on the backend.
Currently the data gets fetched and loads for first page in api(http://localhost:8000/cluster/37/tasks), in my pagination, I want to attempt two trials :
I want when I click on next in the pagination, it should trigger the fetch to load the next page from the api. In this case it would be the second page, so it should fetch from (http://localhost:8000/cluster/37/tasks?page=2)
I have a list of page numbers on the pagination, so when I click on page number four, it should trigger a fetch from page 4 of the api (http://localhost:8000/cluster/37/tasks?page=4)
How could I achieve that. Below is some important part of my code with some more details in it :
class TasksApp extends React.Component {
constructor(props) {
super(props);
this.state = {
columnDefs: [
{ headerName: 'Status', field: 'status' },
{ headerName: 'Started', field: 'rut_creation_time' },
{
headerName: 'Duration', field: 'duration'
},
{ headerName: 'Run', field: 'run_id' },
{ headerName: 'Total Requests', valueGetter: '' },
{
headerName: 'Download',
cellRenderer: 'btnCellRenderer',
cellRendererParams: {
clicked: function (params) {
console.log(params)
}
}
}
],
defaultColDef: { resizable: true },
frameworkComponents: {
btnCellRenderer: BtnCellRenderer,
},
rowData: null,
rowModelType: 'serverSide',
cacheBlockSize: 10,
dataLength: 0,
id: this.props.location.state.id,
headerHeight: 39,
rowHeight: 49,
paginationPageSize: 10,
totalPages: null,
currentPage: null,
pageSize: null,
pageNumberList: [],
pageSizeList: [],
startIndex: 0,
endIndex: 5,
};
}
onGridReady = params => {
this.gridApi = params.api;
const updateData = data => {
this.setState({
rowData: data['results'],//returns the data of the page
dataLength: data['totalDataOnPage'], //returns the length of data in page
totalData: data['totalData'],//returns length of total data
currentPage: data['currentPage'],//returns the current page number
totalPages:data['totalPages'],//returns the total pages
nextLink: data['nextPage'],//contains the next link
previousLink : data['previousPage']//contains the previous link
})
var Server = createServer(this.state.rowData);
var datasource = createServerSideDatasource(Server);
params.api.setServerSideDatasource(datasource);
};
fetch(`http://localhost:8000/cluster/${this.state.id}/tasks`, options)
.then(res => res.json())
.then(data => updateData(data))
.catch(err => console.log(err))
};
onBtNext = () => {
//how do I attempt this to lead to the next page (fetch from http://localhost:8000/cluster/37/tasks?page=<next-page>)
};
gotoPage = () => {
//how do I attempt this to lead to a specific page. for example if I click on page 4, it should trigger fetch from `http://localhost:8000/cluster/37/tasks?page=4`
}
render() {
console.log('data', this.state.rowData)
console.log('pages', this.state.totalPages)
console.log('next', this.state.nextLink)
console.log('previous', this.state.previousLink)
return (
<>
<Wrapper>
<InnerWrapper>
<TableContent>
<Fragment>
<FullScreenDataGrid style={{ flexDirection: 'column', flex: '1 1 0%' }}>
<div className="ag-matrix" style={{ position: 'absolute', left: '0px', top: '0px', right: '0px', bottom: '0px' }}>
<AgGridReact
columnDefs={this.state.columnDefs}
rowModelType={this.state.rowModelType}
cacheBlockSize={this.state.cacheBlockSize}
debug={true}
defaultColDef={this.state.defaultColDef}
headerHeight={this.state.headerHeight}
rowHeight={this.state.rowHeight}
overlayNoRowsTemplate={this.state.overlayNoRowsTemplate}
pagination={true}
// onRowClicked={this.rowClicked}
frameworkComponents={this.state.frameworkComponents}
// paginationPageSize={this.state.paginationPageSize}
onGridReady={this.onGridReady}
suppressPaginationPanel={true}
onPaginationChanged={this.onPaginationChanged.bind(this)}
/>
</div>
</FullScreenDataGrid>
<div className="Pagination-View MatrixGridFooter_DataGridFooter_3NolQ" style={{ height: '64px', marginTop: '2px' }}>
<PaginationSectorTwo>
//previous button comes here
<PageDirection onClick={() => this.onBtPrevious()}>
<ChevronLeft style={{ padding: '5px' }} />
</PageDirection>
//list of page numbers
<PageList>
{
this.state.pageNumberList.slice(this.state.startIndex, this.state.endIndex).map(PageNumber => (
<Page onClick={this.gotoPage} key={PageNumber} value={PageNumber} className={PageNumber === this.state.currentPage ? 'linkactive' : ''}>
{PageNumber}
</Page>
))}
</PageList>
//next button comes here.
<PageDirection onClick={() => this.onBtNext()} style={{marginLeft: '15px'}}>
<ChevronRight style={{ padding: '5px' }} />
</PageDirection>
</PaginationSectorTwo>
</div>
</Fragment>
</TableContent>
</InnerWrapper>
</Wrapper>
</>
)
}
}
Besides I am using ag-grid to display my data from a django rest framework api
Thank you.
It's easier with functional component using Hooks. However, with class component you can approach this by calling the callback function of this.setState({}, () => {//callback})
Here's a quick CodeSandbox example that might help you.

one react component updates all the other react components

I have a react table and one of the columns of it is another component. This component is a dropdown which get its value with an API call which I have defined in componentDidMount().
I have use case where in if user selects any value from the dropdown, I want to save that field to the DB. So I defined this post call in the handleChange function of the dropdown.
Issue is that when I change the value in any one row, every other component in other rows also calls the makes the network calls which is defined in componentDidMount(). So componentDidMount() is called for all the 4 entries. I confirmed on the server side as well. I can see four get requests(I have only 4 rows for now). I am thoroughly confused why it's behaving this way?
Parent Component
import React from 'react';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
import Popup from "reactjs-popup";
export default class DetailsTable extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
shipmentDataMap : { },
selectedRow: null,
downloadableAlerts: []
};
this.setState = this.setState.bind(this);
this.handleRowClick = this.handleRowClick.bind(this);
this.handleReassignment = this.handleReassignment.bind(this);
this.handleStatusUpdate = this.handleStatusUpdate.bind(this);
this.generateFilteredArr = this.generateFilteredArr.bind(this);
this.handleDownload = this.handleDownload.bind(this);
this.updateActualEntity = this.updateActualEntity.bind(this);
};
componentDidMount() {
axios.post('/entity/getRoute', {
trackingId: this.state.tid
})
.then((response) => {
let tempRoute = [];
response.data.route.forEach(element => {
tempRoute.push({ label: element['node'], value: element['node'] });
})
this.setState({route: tempRoute});
})
.catch(function (error) {
console.log(error);
});
};
updateActualEntity = (trackingId, updatedEntity) => {
let updatedRecord = this.state.shipmentDataMap[trackingId];
updatedRecord.actualEntity = updatedEntity;
this.setState({shipmentDataMap: this.state.shipmentDataMap});
};
render() {
const TableColumns = [{
Header: 'Actions',
id: 'actionPopupButton',
filterable: false,
style: {'textAlign': 'left'},
Cell: row => (<div><ReassignPopup data={row.original} updateRowFunc={this.handleReassignment} nodeOptions={this.props.nodeOptions}/>
<br/>
<UpdateStatusPopup data={row.original} updateRowFunc={this.handleStatusUpdate} statusOptions={this.props.statusOptions}/>
</div>)
},
{
Header: 'Assigned Node',
headerStyle: {'whiteSpace': 'unset'},
accessor: 'node',
style: {'whiteSpace': 'unset'}
}, {
Header: 'TID',
headerStyle: {'whiteSpace': 'unset'},
accessor: 'tid',
width: 140,
filterMethod: (filter, row) => {
return row[filter.id].startsWith(filter.value)
},
Cell: props => {props.value}
},
{
Header: 'Predicted Entity',
headerStyle: {'whiteSpace': 'unset'},
filterable: false,
accessor: 'predictedEntity',
style: {'whiteSpace': 'unset'},
},
{
Header: 'Feedback',
headerStyle: {'whiteSpace': 'unset'},
filterable: false,
accessor: 'actualEntity',
width: 140,
style: {'whiteSpace': 'unset', overflow: 'visible'},
Cell: row => (<div><AbusiveEntityComponent entity={row.original.actualEntity}
tid={row.original.tid} trackingDetailsId={row.original.trackingDetailsId}
updateActualEntityInShipmentData={this.updateActualEntity}/></div>)
}
return <div>
<CSVLink data={this.state.downloadableAlerts} filename="ShipmentAlerts.csv" className="hidden" ref={(r) => this.csvLink = r} target="_blank"/>
<ReactTable
ref={(r)=>this.reactTable=r}
className='-striped -highlight'
filterable
data={Object.values(this.state.shipmentDataMap)}
//resolveData={data => data.map(row => row)}
columns={TableColumns}
//filtered={this.state.filtered}
filtered={this.generateFilteredArr(this.props.filterMap, this.props.searchParams)}
/*onFilteredChange={(filtered, column, value) => {
this.onFilteredChangeCustom(value, column.id || column.accessor);
}}*/
defaultFilterMethod={(filter, row, column) => {
const id = filter.pivotId || filter.id;
if (typeof filter.value === "object") {
return row[id] !== undefined
? filter.value.indexOf(row[id].toString()) > -1
: true;
} else {
return row[id] !== undefined
? String(row[id]).indexOf(filter.value) > -1
: true;
}
}}
defaultPageSize={10}
//pageSize={10}
previousText='Previous Page'
nextText='Next Page'
noDataText='No intervention alerts found'
style={{
fontSize: "12px",
height: "67.4vh" // Using fixed pixels/limited height will force the table body to overflow and scroll
}}
getTheadFilterProps={() => {return {style: {display: "none" }}}}
getTbodyProps={() => {return {style: {overflowX: "hidden" }}}} //For preventing extra scrollbar in Firefox/Safari
/*
getTrProps={(state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {this.handleRowClick(e, rowInfo)},
style: {
//background: rowInfo.index === this.state.selectedRow ? '#00afec' : 'white',
color: rowInfo.index === this.state.selectedRow ? 'blue' : 'black'
}
}
} else {
return {}
}
}
} */
/>
</div>;
}
}
Child Component
import React from 'react';
import axios from 'axios';
export default class AbusiveEntityComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
entity: this.props.entity,
tid: this.props.tid,
trackingDetailsId: this.props.trackingDetailsId,
route: []
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
var selected = event.target.value;
if(selected !== '' && this.state.entity !== selected) {
if (window.confirm('Are you sure you want to select: '+ selected)) {
axios.post('/entity/upateAbusiveEntity', {
trackingDetailsId: this.state.trackingDetailsId,
abusiveEntity: selected
}).then( (response) =>{
this.setState({entity: selected});
this.props.updateActualEntityInShipmentData(this.state.tid, selected);
})
.catch(function (error) {
console.log(error);
});
}
}
}
componentDidMount() {
console.log("did mount");
axios.get('/entity/getRoute', {
params: {
trackingId: this.state.tid
}
})
.then((response) => {
let tempRoute = [];
let prev="";
response.data.route.forEach(element => {
if(prev!== "") {
tempRoute.push(prev+"-"+element['node'])
}
tempRoute.push(element['node']);
prev=element['node'];
})
this.setState({route: [''].concat(tempRoute)});
})
.catch(function (error) {
console.log(error);
});
};
render() {
return (
<div className="AbusiveEntityDiv">
<select onChange={this.handleChange} value={this.state.entity===null?'':this.state.entity}
style={{width: 100}}>
{ this.state.route.map(value => <option key={value} value={value}>{value}</option>) }
</select>
</div>
);
}
}
My question is if componentDidUpdate() is not the correct place to fetch data for dropdown, where should I define the network call ?
I found the solution. In the parent component I maintain a state of shipmentstatusmap. One of the columns of this map is acutalEntity. Now in the child component, whenever user selects the value from dropdown, I callback the parent to update the shipmentStatusMap as well. This callback was my problem.
Because now the state of parent component changes, it unmount the child and re-mount it. So it's componentDidMount is called for all the rows which in turn makes the API call.
Solution
Since I want the dropdown values only once when whole parent component is loaded, I can either move the API to constructor or the in the componentDidMount() of parent. Fetching data in constructor is not a good idea .
So I moved this API call in parent and voila! everything works as expected.
updated code:
Child component
import React from 'react';
import axios from 'axios';
export default class AbusiveEntityComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
entity: this.props.entity,
tid: this.props.tid,
trackingDetailsId: this.props.trackingDetailsId,
route: this.props.route
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
var selected = event.target.value;
if(selected !== '' && this.state.entity !== selected) {
if (window.confirm('Are you sure you want to select: '+ selected)) {
axios.post('/entity/upateAbusiveEntity', {
trackingDetailsId: this.state.trackingDetailsId,
abusiveEntity: selected
}).then( (response) =>{
this.setState({entity: selected});
this.props.updateActualEntityInShipmentData(this.state.tid, selected);
})
.catch(function (error) {
console.log(error);
});
}
}
}
render() {
return (
<div className="AbusiveEntityDiv">
<select onChange={this.handleChange} value={this.state.entity===null?'':this.state.entity}
style={{width: 100}}>
{ this.state.route.map(value => <option key={value} value={value}>{value}</option>) }
</select>
</div>
);
}
}
Parent component
import React from 'react';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
import Popup from "reactjs-popup";
export default class DetailsTable extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
shipmentDataMap : { },
selectedRow: null,
downloadableAlerts: []
};
this.setState = this.setState.bind(this);
this.handleRowClick = this.handleRowClick.bind(this);
this.handleReassignment = this.handleReassignment.bind(this);
this.handleStatusUpdate = this.handleStatusUpdate.bind(this);
this.generateFilteredArr = this.generateFilteredArr.bind(this);
this.handleDownload = this.handleDownload.bind(this);
this.updateActualEntity = this.updateActualEntity.bind(this);
};
// this portion was updated
componentDidMount() {
fetch('/shipment/all')
.then(res => res.json())
.then(shipmentList => {
var tidToShipmentMap = {};
var totalShipmentCount = shipmentList.length;
var loadedShipmentRoute = 0;
shipmentList.forEach(shipment => {
axios.get('/entity/getRoute', {
params: {
trackingId: shipment.tid
}
})
.then(response => {
let tempRoute = [];
let prev="";
response.data.route.forEach(element => {
if(prev!== "") {
tempRoute.push(prev+"-"+element['node'])
}
tempRoute.push(element['node']);
prev=element['node'];
})
shipment.route = [''].concat(tempRoute);
tidToShipmentMap[shipment.tid] = shipment;
loadedShipmentRoute++;
if (loadedShipmentRoute === totalShipmentCount) {
this.setState({ shipmentDataMap: tidToShipmentMap});
console.log(tidToShipmentMap);
}
})
.catch(function (error) {
console.log(error);
});
});
})
.catch(error => console.log(error));
};
updateActualEntity = (trackingId, updatedEntity) => {
let updatedRecord = this.state.shipmentDataMap[trackingId];
updatedRecord.actualEntity = updatedEntity;
this.setState({shipmentDataMap: this.state.shipmentDataMap});
};
render() {
const TableColumns = [{
Header: 'Actions',
id: 'actionPopupButton',
filterable: false,
style: {'textAlign': 'left'},
Cell: row => (<div><ReassignPopup data={row.original} updateRowFunc={this.handleReassignment} nodeOptions={this.props.nodeOptions}/>
<br/>
<UpdateStatusPopup data={row.original} updateRowFunc={this.handleStatusUpdate} statusOptions={this.props.statusOptions}/>
</div>)
},
{
Header: 'Assigned Node',
headerStyle: {'whiteSpace': 'unset'},
accessor: 'node',
style: {'whiteSpace': 'unset'}
}, {
Header: 'TID',
headerStyle: {'whiteSpace': 'unset'},
accessor: 'tid',
width: 140,
filterMethod: (filter, row) => {
return row[filter.id].startsWith(filter.value)
},
Cell: props => {props.value}
},
{
Header: 'Predicted Entity',
headerStyle: {'whiteSpace': 'unset'},
filterable: false,
accessor: 'predictedEntity',
style: {'whiteSpace': 'unset'},
},
{
Header: 'Feedback',
headerStyle: {'whiteSpace': 'unset'},
filterable: false,
accessor: 'actualEntity',
width: 140,
style: {'whiteSpace': 'unset', overflow: 'visible'},
Cell: row => (<div><AbusiveEntityComponent entity={row.original.actualEntity}
tid={row.original.tid} trackingDetailsId={row.original.trackingDetailsId}
updateActualEntityInShipmentData={this.updateActualEntity}/></div>)
}
return <div>
<CSVLink data={this.state.downloadableAlerts} filename="ShipmentAlerts.csv" className="hidden" ref={(r) => this.csvLink = r} target="_blank"/>
<ReactTable
ref={(r)=>this.reactTable=r}
className='-striped -highlight'
filterable
data={Object.values(this.state.shipmentDataMap)}
//resolveData={data => data.map(row => row)}
columns={TableColumns}
//filtered={this.state.filtered}
filtered={this.generateFilteredArr(this.props.filterMap, this.props.searchParams)}
/*onFilteredChange={(filtered, column, value) => {
this.onFilteredChangeCustom(value, column.id || column.accessor);
}}*/
defaultFilterMethod={(filter, row, column) => {
const id = filter.pivotId || filter.id;
if (typeof filter.value === "object") {
return row[id] !== undefined
? filter.value.indexOf(row[id].toString()) > -1
: true;
} else {
return row[id] !== undefined
? String(row[id]).indexOf(filter.value) > -1
: true;
}
}}
defaultPageSize={10}
//pageSize={10}
previousText='Previous Page'
nextText='Next Page'
noDataText='No intervention alerts found'
style={{
fontSize: "12px",
height: "67.4vh" // Using fixed pixels/limited height will force the table body to overflow and scroll
}}
getTheadFilterProps={() => {return {style: {display: "none" }}}}
getTbodyProps={() => {return {style: {overflowX: "hidden" }}}} //For preventing extra scrollbar in Firefox/Safari
/*
getTrProps={(state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {this.handleRowClick(e, rowInfo)},
style: {
//background: rowInfo.index === this.state.selectedRow ? '#00afec' : 'white',
color: rowInfo.index === this.state.selectedRow ? 'blue' : 'black'
}
}
} else {
return {}
}
}
} */
/>
</div>;
}
}

React setState not rendering view of bootstrap table

I have this component that has a Bootstrap Table and a Modal. User should be able to go into the modal and change the state of the same data for both the table and the modal; however, I am seeing that it is only changing the view in the modal but not the table?
Component with Table and Modal:
export class TableAndModal extends React.Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
showModal: false,
index: ""
}
};
this.setShow = this.setShow.bind(this);
this.handleShowAndChange = this.handleShowAndChange.bind(this);
}
columns = [{
dataField: "view",
text: "View"
formatter: (cell, row, rowIndex) => {
return (
<div>
<Button variant="info" onClick={() => this.setShow(rowIndex)}>View</Button>
</div>
);
}
},
{dataField: 'fullName', text: 'Full Name' },
{dataField: 'studentDesc', text: 'Student Description'},
{dataField: 'email', text: 'Email'},
{dataField: 'fullNotes', text: 'Full Notes'},
{dataField: 'edu', text: 'Education'},
{dataField: 'phone', text: 'Phone Number'},
{dataField: 'id', text: 'ID'}];
setShow(index) {
this.setState({
showModal: true,
index: index
});
}
handleShowAndChange = (name, value) => {
this.setState((prevState) => {
let newState = {...prevState};
newState[name] = value;
return newState;
});
};
render() {
return (
<div>
<BootstrapTable
hover
condensed={true}
bootstrap4={true}
keyField={'id'}
data={this.state.data.info}
columns={this.columns}
/>
<Modal data={this.state.data.info} index={this.state.index}
showModal={this.state.showModal} onChange={this.handleShowAndChange} />
</div>
);
}
}
Modal:
this.state = {
data: this.props.data
};
handleInfoChange = (index) => (name, value) => {
let info = this.state.data.info.slice();
info[index][name] = value;
this.props.onChange("info", info);
};
I am seeing that the state is being modified correctly. However, the table still has the same view with the old state data even though the state has been changed.
Can someone guide me on what I am doing wrong?
I think you should use props.data instead of this.props.data
constructor(props) {
super(props);
this.state = {
data: props.data,
showModal: false,
index: ""
}
}

Resources