具有react和material-ui TablePagination的猫鼬分页-单击onChangePage属性

时间:2019-01-30 08:36:17

标签: node.js reactjs mongodb express material-ui

我对React还是比较陌生的,在这个React组件中,我必须缺少一些基本知识。非常感谢您提供有关该解决方案的指导。

我要在此处实现的功能-使用后端的mongoose-paginate和前端的Material-UI TablePagination 实现分页结束。因此,每次用户单击下一页图标(向前或向后箭头)时,都会使用axios.get进行数据库调用,并且将从mongo中获取数据并将其呈现在UI的表格中。

要实现这一点,我在React中有handleChangePage()函数,它将调用axios.get函数。

问题-从第二页开始,数据没有重新发布,但是通过console.log,我可以看到(在chrome-devtool中)单击后确实从后端获取了数据下一页图标。但是,获取的数据不会在UI中呈现。

这是我的渲染组件中的React代码。关键功能是 handleChangePage(),这是我每次都要执行数据库调用以获取数据的位置,用户单击下一页图标(下一页图标是Material-UI的 TablePagination 组件)

class List extends Component {
  constructor(props) {
    super(props);
    this.state = {
      allDevelopmentWorks: [],
      allDevelopmentWorksFormatted: [],      
      selected: [],
      page: 0,
      rowsPerPage: 5,      
      renderOnlyDateRangeData: false,
      developmentWorksDateRange: []
    };
  }

  // Function to handle the the request from user to sort by a particular heading.
  handleRequestSort = (event, property) => {
    const orderBy = property;
    let order = "desc";

    if (this.state.orderBy === property && this.state.order === "desc") {
      order = "asc";
    }    
    this.setState({ order, orderBy });
  };

  handleSelectAllClick = event => {
    if (event.target.checked) {
      this.setState(state => ({
        selected: state.allDevelopmentWorks.map(n => n._id)
      }));
      return;
    }
    this.setState({ selected: [] });
  };

  handleClick = (event, id) => {
    const { selected } = this.state;
    const selectedIndex = selected.indexOf(id);
    let newSelected = [];

    if (selectedIndex === -1) {
      newSelected = newSelected.concat(selected, id);
    } else if (selectedIndex === 0) {
      newSelected = newSelected.concat(selected.slice(1));
    } else if (selectedIndex === selected.length - 1) {
      newSelected = newSelected.concat(selected.slice(0, -1));
    } else if (selectedIndex > 0) {
      newSelected = newSelected.concat(
        selected.slice(0, selectedIndex),
        selected.slice(selectedIndex + 1)
      );
    }
    this.setState({ selected: newSelected });
  };

  componentDidMount() {
    axios
      .get("/api/developmenties/")
      .then(res => {
        this.setState(
          {
            allDevelopmentWorksFormatted: res.data
          },
          () => {
            this.state.allDevelopmentWorksFormatted.map((item, index) => {
              if (item.date_of_commencement || item.date_of_completion) {
                this.state.allDevelopmentWorksFormatted[
                  index
                ].date_of_commencement =
                  moment(item.date_of_commencement).format(
                    "MMM D, YYYY 12:00:00 "
                  ) + `AM`;
                this.state.allDevelopmentWorksFormatted[
                  index
                ].date_of_completion =
                  moment(item.date_of_completion).format(
                    "MMM D, YYYY 12:00:00 "
                  ) + `AM`;
              }
            });
          }
        );
      })
      .then(
        axios
          .get("/api/developmenties/paginate", {
            params: {
              page: this.state.page,
              rowsperpage: this.state.rowsPerPage
            }
          })
          .then(res => {
            console.log("THE RESPONSE FOR PAGINATION IS ", res.data);
            this.setState({
              allDevelopmentWorks: res.data
            });
          })
      )
      .catch(function(error) {
        console.log(error);
      });
  }

  componentDidUpdate(prevProps, prevState) {
    if (

       this.state.developmentWorksDateRange.length !==
    prevState.developmentWorksDateRange.length ||
  this.state.allDevelopmentWorksFormatted.length !==
    prevState.allDevelopmentWorksFormatted.length ||
  this.state.rowsPerPage !== prevState.rowsPerPage

    ) {
      return axios
        .get("/api/developmenties/paginate", {
          params: {
            page: this.state.page,
            rowsperpage: this.state.rowsPerPage
          }
        })
        .then(res => {
          this.setState({
            allDevelopmentWorks: res.data
          });
        })
        .catch(function(error) {
          console.log(error);
        });
    }
  }


  handleChangePage = async (event, page) => {
    // console.log("THIS PAGE NO IS ", page);
    await this.setState({ page });
    const res = await axios.get(`/api/developmenties/paginate`, {
      params: {
        page: page,
        rowsperpage: this.state.rowsPerPage
      }
    });
    this.setState({
      allDevelopmentWorks: res.data
    });    
  };

  handleChangeRowsPerPage = event => {
    this.setState({ rowsPerPage: event.target.value });
  };

  isSelected = id => this.state.selected.indexOf(id) !== -1;

  unSelectItems = () => {
    this.setState({
      selected: []
    });
  };

  handleQueryString = queryTypedInChild => {
    this.setState({
      queryStringFromChild: queryTypedInChild
    });
  };

  handleColumnToQuery = columnToQueryInChild => {
    this.setState({
      columnToQuery: columnToQueryInChild
    });
  };

  clearAllQueryString = () => {
    this.setState({
      queryStringFromChild: "",
      columnToQuery: "location"
    });
  };

  ifUserWantsDateRangeData = dateRangeArr => {
    this.setState({
      developmentWorksDateRange: [...dateRangeArr]
    });
  };

  render() {
    const { classes } = this.props;
    const {
      order,
      orderBy,
      selected,
      rowsPerPage,
      page,
      allDevelopmentWorks,
      developmentWorksDateRange,
      allDevelopmentWorksFormatted,
      queryStringFromChild
    } = this.state;


    const emptyRows =
      rowsPerPage -
      Math.min(rowsPerPage, allDevelopmentWorks.length - page * rowsPerPage);

    // in below the whole table header is a different component 'EnhancedTableHead'
    return (
      <MuiThemeProvider>
        <div>
          <Row>
            <Col xs="12">
              {console.log(
                "CURRENT DEVELOPMENT LIST RENDERED IS  ",
                allDevelopmentWorks
              )}

              <Paper className={classes.root}>                
                <Table className={classes.table}>                  
                  <TableBody>
                    {stableSort(
                      allDevelopmentWorks,
                      getSorting(order, orderBy)
                    )
                      .slice(
                        page * rowsPerPage,
                        page * rowsPerPage + rowsPerPage
                      )
                      .map(n => {
                        const isSelected = this.isSelected(n._id);
                        return (
                          <TableRow
                            hover
                            onClick={event => this.handleClick(event, n._id)}
                            role="checkbox"
                            aria-checked={isSelected}
                            tabIndex={-1}
                            key={n._id}
                            selected={isSelected}
                          >
                            <CustomTableCell padding="checkbox">
                              <Checkbox checked={isSelected} />
                            </CustomTableCell>
                            <CustomTableCell
                              component="th"
                              scope="row"
                              padding="none"
                            >
                              {n.location}
                            </CustomTableCell>
                            <CustomTableCell align="right">
                              {n.work_description}
                            </CustomTableCell>
                            <CustomTableCell align="right">
                              {moment(n.date_of_commencement).format(
                                "MMM D, YYYY 12:00:00 "
                              )}{" "}
                              {`AM`}
                            </CustomTableCell>
                            <CustomTableCell align="right">
                              {moment(n.date_of_completion).format(
                                "MMM D, YYYY 12:00:00 "
                              )}{" "}
                              {`AM`}
                            </CustomTableCell>
                            <CustomTableCell align="right">
                              {n.status_of_work}
                            </CustomTableCell>
                          </TableRow>
                        );
                      })}
                    {emptyRows > 0 && (
                      <TableRow style={{ height: 49 * emptyRows }}>
                        <CustomTableCell colSpan={6} />
                      </TableRow>
                    )}
                  </TableBody>
                </Table>                
                <TablePagination
                  rowsPerPageOptions={[5, 10, 25]}
                  component="div"
                  count={allDevelopmentWorksFormatted.length}
                  rowsPerPage={rowsPerPage}
                  page={page}
                  backIconButtonProps={{
                    "aria-label": "Previous Page"
                  }}
                  nextIconButtonProps={{
                    "aria-label": "Next Page"
                  }}
                  onChangePage={this.handleChangePage}
                  onChangeRowsPerPage={this.handleChangeRowsPerPage}
                />
              </Paper>
            </Col>
          </Row>
          <Row>
            <br />
          </Row>          
        </div>
      </MuiThemeProvider>
    );
  }
}

List.propTypes = {
  classes: PropTypes.object.isRequired
};

export default withStyles(styles)(List);

据我所知,问题的根源是在其中处理两个setState的 handleChangePage()函数。第一个setstate this.setState({page})正在触发重新渲染,并使列表仅使用当前数据重新渲染(即,在此函数中的下一个axios.get请求被触发之前)并更新变量 allDevelopmentWorks

handleChangePage = async (event, page) => {       
    await this.setState({ page });
    await axios
      .get(`/api/developmenties/paginate`, {
        params: {
          page: this.state.page,
          rowsperpage: this.state.rowsPerPage
        }
      })
      .then(res => {
        this.setState({
          allDevelopmentWorks: res.data
        });
        console.log(
          "AFTER SETSTATE UPDATED ALL-DEVELOPMENTWORKS IS ",
          this.state.allDevelopmentWorks
        );
      })
      .catch(function(error) {
        console.log(error);
      });
  };

因此,我尝试仅在使用以下代码的this.setState({ page })之后停止重新渲染组件,但未解决我的问题

  shouldComponentUpdate(nextProps, nextState) {
    if (this.state.page !== nextState.page) {
      return false;
    }
    return true;
  }

以下是我在Express中用于从mongo数据库获取数据的后端路由代码。开发是我的猫鼬模式的名字。

// To GET ALL DevelopmentWorks - max 5 or 10 or 15 at a time (determined by whatever users sets it to be at the front-end MAT-UI)

router.get("/paginate", (req, res, next) => {
  console.log("THE REQ.QUERY FOR PAGINATION IS ", req.query);
  let pageOptions = {
    page: parseInt(req.query.page) || 0,
    limit: parseInt(req.query.rowsperpage) || 5
  };

  if (req.query.page && req.query.rowsperpage) {
    Development.paginate(
      {},
      {
        offset: pageOptions.page * pageOptions.limit,
        limit: pageOptions.limit
      },
      (err, result) => {
        if (err) {
          console.log(err);
          next(err);
        } else {
          res.status(200).json(result.docs);
        }
      }
    );
  }
});

// To GET ALL DevelopmentWorks
router.route("/").get((req, res, next) => {
  Development.find(
    {},
    null,
    {
      sort: { createdAt: -1 }
    },
    (err, docs) => {
      // Development.find({ port: req.body.port._id }, (err, docs) => {
      if (err) {
        return next(err);
      } else {
        res.status(200).json(docs);
      }
    }
  );
});

更多说明-当我不使用 mongoose-paginate 并加载整个数据(通过单次调用将其获取到数据库)时, TablePaginaton 就可以了正确地。但是现在要实现分页,我希望每次用户单击下一页图标时都进行新的服务器调用。

1 个答案:

答案 0 :(得分:1)

在解决问题后,回答我自己的问题。问题是我将数据切片两次以在表中呈现。因此,在整合了mongoose-paginate的情况下,切片工作是由后端本身完成的,因为mongoose-paginate只会将切片的数据发送到前端。但是我的错误是,在渲染表期间,我再次进行切片(这是在整合mongoosse-paginate之前一次获取全部数据时所需的)。这是上面代码的更正部分(即,我将表放到return()内的地方)。

<TableBody>
{stableSort(
  allDevelopmentWorks,
  getSorting(order, orderBy)
)
  .map(n => {
    const isSelected = this.isSelected(n._id);
    return (
      <TableRow
        hover
        onClick={event => this.handleClick(event, n._id)}
        role="checkbox"
        aria-checked={isSelected}
        tabIndex={-1}
        key={n._id}
        selected={isSelected}
      >
        <CustomTableCell padding="checkbox">
          <Checkbox checked={isSelected} />
        </CustomTableCell>
        <CustomTableCell
          component="th"
          scope="row"
          padding="none"
        >
          {n.location}
        </CustomTableCell>
        <CustomTableCell align="right">
          {n.work_description}
        </CustomTableCell>
        <CustomTableCell align="right">
          {moment(n.date_of_commencement).format(
            "MMM D, YYYY 12:00:00 "
          )}{" "}
          {`AM`}
        </CustomTableCell>
        <CustomTableCell align="right">
          {moment(n.date_of_completion).format(
            "MMM D, YYYY 12:00:00 "
          )}{" "}
          {`AM`}
        </CustomTableCell>
        <CustomTableCell align="right">
          {n.status_of_work}
        </CustomTableCell>
      </TableRow>
    );
  })}
{emptyRows > 0 && (
  <TableRow style={{ height: 49 * emptyRows }}>
    <CustomTableCell colSpan={6} />
  </TableRow>
)}
</TableBody>

我只需要删除零件

.slice(
          page * rowsPerPage,
          page * rowsPerPage + rowsPerPage
                      )