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 道具后第二页开始不呈现的主要内容,如果未能解决你的问题,请参考以下文章
有啥区别(从“react”导入React;)与(从“react”导入React;)[重复]
React.js 与 ASP MVC 与 create-react-app 集成
import * as react from 'react' 与 import react from 'react' 有啥区别