Я относительно новичок в React, и в этом компоненте React мне не хватает чего-то базового.Буду очень признателен за любые рекомендации по решению.
Функциональность Я пытаюсь достичь здесь - Реализация нумерации страниц с мангуста-пагинат в бэкэнде и Material-UI TablePagination во внешнем интерфейсе.Поэтому каждый раз, когда пользователь нажимает на значок следующей страницы (стрелка вперед или назад), происходит обращение к базе данных с помощью axios.get, и данные будут извлекаться из mongo и отображаться в таблице в пользовательском интерфейсе.
Для этого у меня есть функция handleChangePage () в React, которая будет вызывать функцию axios.get.
Проблема - Начиная со второй страницы данные не обновляются, нос помощью console.log я вижу (в chrome-devtool), что данные действительно были получены из бэкэнда после нажатия на значок следующей страницы.Но эти извлеченные данные не отображаются в пользовательском интерфейсе.
Это код React в моем компоненте рендеринга.Ключевой функцией является handleChangePage () , здесь я выполняю вызов базы данных для извлечения данных каждый раз, когда пользователь нажимает значок следующей страницы (значок следующей страницы - это Material-UI TablePagination component)
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 () при обработке двух 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 для извлечения данных из базы данных Монго.Development - это название моей схемы mongoose.
// 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 работал правильно.Но теперь, чтобы реализовать нумерацию страниц, я хочу делать новый серверный вызов каждый раз, когда пользователь нажимает на значок следующей страницы.