一个反应组件更新所有其他反应组件

时间:2019-10-01 09:39:14

标签: reactjs react-table

我有一个反应表,其中的一列是另一个组件。该组件是一个下拉列表,通过我在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()是否不是获取下拉数据的正确位置,我应该在哪里定义网络调用?

1 个答案:

答案 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>;
  }
}