mongoose-paginate 与 react 和 material-ui TablePagination - 单击 onChangePage 道具后第二页开始不呈现

Posted

技术标签:

【中文标题】mongoose-paginate 与 react 和 material-ui TablePagination - 单击 onChangePage 道具后第二页开始不呈现【英文标题】:mongoose-paginate with react and material-ui TablePagination - second page onwards not rendering after clicking of onChangePage prop 【发布时间】:2019-06-23 11:34:46 【问题描述】:

我对 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);

据我所知,问题的根源是 handleChangePage() 函数在处理 this 中的两个 setState。第一个 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】:

在我解决问题后回答我自己的问题。问题是我将数据切片以在表中呈现两次。因此,集成了 mongoose-paginate 后,切片工作由后端自己完成,因为 mongoose-paginate 只会将切片数据发送到前端。但我的错误是,在表格的呈现过程中,我再次切片(正如我在集成 mongoosse-paginate 之前一次获取整个数据时所需要的那样)。这是上述代码的更正部分(即我在 return() 中渲染 Table 的地方)。

<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
                      )

【讨论】:

这对我有用,我删除了 TableBody 中的 .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) 以修复服务器端分页。非常感谢!

以上是关于mongoose-paginate 与 react 和 material-ui TablePagination - 单击 onChangePage 道具后第二页开始不呈现的主要内容,如果未能解决你的问题,请参考以下文章

mongoose-paginate 按引用文档排序

像使用 mongoose-paginate 的过滤器

在插件中导入架构后调用“分页”时出错

有啥区别(从“react”导入React;)与(从“react”导入React;)[重复]

React.js 与 ASP MVC 与 create-react-app 集成

import * as react from 'react' 与 import react from 'react' 有啥区别