我有一个反应表,其中的一列是另一个组件。该组件是一个下拉列表,通过我在componentDidMount()中定义的API调用获得其值。
我有一个用例,如果用户从下拉列表中选择任何值,我想将该字段保存到数据库。因此,我在下拉菜单的handleChange
函数中定义了该帖子调用。
问题是,当我更改任一行中的值时,其他行中的每个其他组件也会调用在componentDidMount()中定义的网络调用。因此,将为所有4个条目调用componentDidMount()。我也确认了服务器端。我可以看到四个get请求(目前我只有4行)。我完全感到困惑,为什么会这样?
父组件
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 => <a href={`https://eagleeye-eu.amazon.com/search?type=Scannable&display=leg&value=${props.value}`} target="_blank" rel="noopener noreferrer">{props.value}</a>
},
{
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>;
}
}
子组件
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>
);
}
}
我的问题是,componentDidUpdate()是否不是获取下拉数据的正确位置,我应该在哪里定义网络调用?
答案 0 :(得分:0)
我找到了解决方案。在父组件中,我维护一个状态为shippstatusmap的状态。该映射的列之一是acutalEntity。现在,在子组件中,每当用户从下拉列表中选择值时,我都会回调父组件以更新shippingStatusMap。这个回调是我的问题。
由于现在父组件的状态发生了变化,因此它将卸载子组件并重新安装它。因此,将对所有行调用componentDidMount,从而进行API调用。
解决方案
由于加载整个父组件时只需要一次下拉值,因此可以将API移动到构造函数中,也可以将其移到父组件的componentDidMount()中。在构造函数中获取数据不是一个好主意。
因此,我将此API调用移到了parent和voila中!一切都按预期进行。
更新的代码:
子组件
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>
);
}
}
父项
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 => <a href={`https://eagleeye-eu.amazon.com/search?type=Scannable&display=leg&value=${props.value}`} target="_blank" rel="noopener noreferrer">{props.value}</a>
},
{
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>;
}
}