#reactjs #pagination #react-hooks
Вопрос:
У меня есть код ReactJS для разбиения на страницы, и он работает очень хорошо (спасибо за то, что поделился Джейсоном Уотмором). В коде используются компоненты жизненного цикла ReactJS, которые будут монтироваться и обновляться.
Я пытаюсь изменить код, чтобы он работал в ReactJS Hook. Пожалуйста, помогите проверить, где я ошибаюсь. Сравнение кода приведено ниже:
import React from 'react';
import PropTypes from 'prop-types';
const propTypes = {
items: PropTypes.array.isRequired,
onChangePage: PropTypes.func.isRequired,
initialPage: PropTypes.number,
pageSize: PropTypes.number
}
const defaultProps = {
initialPage: 1,
pageSize: 10
}
class Pagination extends React.Component {
constructor(props) {
super(props);
this.state = { pager: {} };
}
componentWillMount() {
// set page if items array isn't empty
if (this.props.items amp;amp; this.props.items.length) {
this.setPage(this.props.initialPage);
}
}
componentDidUpdate(prevProps, prevState) {
// reset page if items array has changed
if (this.props.items !== prevProps.items) {
this.setPage(this.props.initialPage);
}
}
setPage(page) {
var { items, pageSize } = this.props;
var pager = this.state.pager;
if (page < 1 || page > pager.totalPages) {
return;
}
// get new pager object for specified page
pager = this.getPager(items.length, page, pageSize);
// get new page of items from items array
var pageOfItems = items.slice(pager.startIndex, pager.endIndex 1);
// update state
this.setState({ pager: pager });
// call change page function in parent component
this.props.onChangePage(pageOfItems);
}
getPager(totalItems, currentPage, pageSize) {
// default to first page
currentPage = currentPage || 1;
// default page size is 10
pageSize = pageSize || 10;
// calculate total pages
var totalPages = Math.ceil(totalItems / pageSize);
var startPage, endPage;
if (totalPages <= 10) {
// less than 10 total pages so show all
startPage = 1;
endPage = totalPages;
} else {
// more than 10 total pages so calculate start and end pages
if (currentPage <= 6) {
startPage = 1;
endPage = 10;
} else if (currentPage 4 >= totalPages) {
startPage = totalPages - 9;
endPage = totalPages;
} else {
startPage = currentPage - 5;
endPage = currentPage 4;
}
}
// calculate start and end item indexes
var startIndex = (currentPage - 1) * pageSize;
var endIndex = Math.min(startIndex pageSize - 1, totalItems - 1);
// create an array of pages to ng-repeat in the pager control
var pages = [...Array((endPage 1) - startPage).keys()].map(i => startPage i);
// return object with all pager properties required by the view
return {
totalItems: totalItems,
currentPage: currentPage,
pageSize: pageSize,
totalPages: totalPages,
startPage: startPage,
endPage: endPage,
startIndex: startIndex,
endIndex: endIndex,
pages: pages
};
}
render() {
var pager = this.state.pager;
if (!pager.pages || pager.pages.length <= 1) {
// don't display pager if there is only 1 page
return null;
}
return (
<ul className="pagination">
<li className={pager.currentPage === 1 ? 'disabled' : ''}>
<a onClick={() => this.setPage(1)}>First</a>
</li>
<li className={pager.currentPage === 1 ? 'disabled' : ''}>
<a onClick={() => this.setPage(pager.currentPage - 1)}>Previous</a>
</li>
{pager.pages.map((page, index) =>
<li key={index} className={pager.currentPage === page ? 'active' : ''}>
<a onClick={() => this.setPage(page)}>{page}</a>
</li>
)}
<li className={pager.currentPage === pager.totalPages ? 'disabled' : ''}>
<a onClick={() => this.setPage(pager.currentPage 1)}>Next</a>
</li>
<li className={pager.currentPage === pager.totalPages ? 'disabled' : ''}>
<a onClick={() => this.setPage(pager.totalPages)}>Last</a>
</li>
</ul>
);
}
}
Pagination.propTypes = propTypes;
Pagination.defaultProps = defaultProps;
export default Pagination;
Затем я пытаюсь преобразовать в крючок ReactJS, как показано ниже:
import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
function Pagination() {
const [ pager, setPager ] = useState([]);
useEffect(() => {
if(this.props.items amp;amp; this.props.items.length) {
this.setPage(this.props.initialPage);
}
},[]);
const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
const setPage = (page) => {
var { items, pageSize } = this.props;
var paging = pager;
//Remove to make it work with search function
//if (page < 1 || page > pager.totalPages) {
// return;
//}
paging = this.getPager(items.length, page, pageSize);
var pageOfItems = items.slice(paging.startIndex, paging.endIndex 1);
setPager(paging);
this.props.onChangePage(pageOfItems);
}
const getPager = (totalItems, currentPage, pageSize) => {
currentPage = currentPage || 1;
pageSize = pageSize || 25;
var totalPages = Math.ceil(totalItems / pageSize);
var startPage, endPage;
if (totalPages <= 10) {
startPage = 1;
endPage = totalPages;
} else {
if (currentPage <= 6) {
startPage = 1;
endPage = 10;
} else if (currentPage 4 >= totalPages) {
startPage = totalPages - 9;
endPage = totalPages;
} else {
startPage = currentPage - 5;
endPage = currentPage 4;
}
}
var startIndex = (currentPage - 1) * pageSize;
var endIndex = Math.min(startIndex pageSize - 1, totalItems - 1);
var pages = [...Array((endPage 1) - startPage).keys()].map(i => startPage i);
return {
totalItems: totalItems,
currentPage: currentPage,
pageSize: pageSize,
totalPages: totalPages,
startPage: startPage,
endPage: endPage,
startIndex: startIndex,
endIndex: endIndex,
pages: pages
};
}
const prevPager = usePrevious(pager);
if (this.props.items !== prevPager){
this.setPage(this.props.initialPage);
}
var pagerr = pager;
if (!pagerr.pages || pagerr.pages.length <= 1) {
return null;
}
return (
<ul className="pagination">
<li className={pagerr.currentPage === 1 ? 'disabled' : ''}>
<a onClick={() => this.setPage(1)}>First</a>
</li>
<li className={pagerr.currentPage === 1 ? 'disabled' : ''}>
<a onClick={() => this.setPage(pagerr.currentPage - 1)}>Previous</a>
</li>
{pagerr.pages.map((page, index) =>
<li key={index} className={pagerr.currentPage === page ? 'active' : ''}>
<a onClick={() => this.setPage(page)}>{page}</a>
</li>
)}
<li className={pagerr.currentPage === pagerr.totalPages ? 'disabled' : ''}>
<a onClick={() => this.setPage(pagerr.currentPage 1)}>Next</a>
</li>
<li className={pagerr.currentPage === pagerr.totalPages ? 'disabled' : ''}>
<a onClick={() => this.setPage(pagerr.totalPages)}>Last</a>
</li>
</ul>
);
}
Pagination.propTypes = {
items: PropTypes.array.isRequired,
onChangePage: PropTypes.func.isRequired,
initialPage: PropTypes.number,
pageSize: PropTypes.number
}
Pagination.defaultProps = {
initialPage: 1,
pageSize: 25
}
export default Pagination;
I have successfully put pagination function to AssessmentList.js
import React, { useState } from "react";
import { useQuery } from "@apollo/react-hooks";
import { gql } from "apollo-boost";
const QUERY = gql`
{
assessments (limit: 84000, sort: "item:asc") {
id
item
title
label
description
image {
url
}
}
}
`;
function AssessmentList(props) {
const { loading, error, data } = useQuery(QUERY);
const [ page, setPage ] = useState(1);
const getPager = (totalItems, currentPage, pageSize) => {
currentPage = currentPage || 1;
pageSize = pageSize || 25;
var totalPages = Math.ceil(totalItems / pageSize);
var startPage, endPage;
if (totalPages <= 10) {
startPage = 1;
endPage = totalPages;
} else {
if (currentPage <= 6) {
startPage = 1;
endPage = 10;
} else if (currentPage 4 >= totalPages) {
startPage = totalPages - 9;
endPage = totalPages;
} else {
startPage = currentPage - 5;
endPage = currentPage 4;
}
}
var startIndex = (currentPage - 1) * pageSize;
var endIndex = Math.min(startIndex pageSize - 1, totalItems - 1);
var pages = [...Array((endPage 1) - startPage).keys()].map(i => startPage i);
return {
totalItems: totalItems,
currentPage: currentPage,
pageSize: pageSize,
totalPages: totalPages,
startPage: startPage,
endPage: endPage,
startIndex: startIndex,
endIndex: endIndex,
pages: pages
};
}
if (error) return "Error loading assessments";
if (loading) return <h1>Fetching</h1>;
if (data.assessments amp;amp; data.assessments.length) {
const searchQuery = data.assessments.filter((query) => {
return Object.keys(query).some(key => query[key].toString().toLowerCase().includes(props.search))
});
const pageSize = 24;
const paging = getPager(searchQuery.length, page, pageSize);
const pageOfItems = (searchQuery.slice(paging.startIndex, paging.endIndex 1));
if (pageOfItems.length != 0) {
return (
<div>
<ul className="image-list">
{pageOfItems.map((res) => (
<li key={res.id} className="image-list__item">
<div className="image__flow thumbnail">
<img src={`${process.env.NEXT_PUBLIC_API_URL}${res.image.url}`} alt="assessment" />
<h4>{res.title}</h4>
<h4>Label</h4>
<h4>{res.label}</h4>
<h5>Assetcode</h5>
<h5>{res.item}</h5>
</div>
</li>
))}
</ul>
<nav aria-label="Page navigation example">
<ul className="pagination" style={{cursor: 'pointer'}}>
<li className={paging.currentPage == 1 ? "page-item disabled" : "page-item"}>
<a className="page-link" onClick={() => setPage(1)}>First</a>
</li>
<li className={paging.currentPage == 1 ? "page-item disabled" : "page-item"}>
<a className="page-link" onClick={() => setPage(paging.currentPage - 1)}>Prev</a>
</li>
{paging.pages.map((page, index) =>
<li key={index} className="page-item">
<a className="page-link" onClick={() => setPage(page)}>{page}</a>
</li>
)}
<li className={paging.currentPage == paging.totalPages ? "page-item disabled" : "page-item"}>
<a className="page-link" onClick={() => setPage(paging.currentPage 1)}>Next</a>
</li>
<li className={paging.currentPage == paging.totalPages ? "page-item disabled" : "page-item"}>
<a className="page-link" onClick={() => setPage(paging.totalPages)}>Last</a>
</li>
</ul>
</nav>
</div>
);
} else {
return <h1>No Assessment</h1>;
}
}
}
export default AssessmentList;
Но функция поиска не работает при переходе на другую страницу.
Например, когда я перехожу на страницу 7, я не могу выполнить поиск элемента на странице 1-6.
Я подумываю использовать useEffect, но он отображается только один раз, и я больше не могу искать. Если я использую эффект использования, когда я обновляю страницу, все также исчезает!!!???
Успешно загрузите, отобразите, обновите следующий код
import React, { useState, useEffect } from "react";
import { useQuery } from "@apollo/react-hooks";
import { gql } from "apollo-boost";
const QUERY = gql`
{
assessments (limit: 84000, sort: "item:asc") {
id
item
title
label
description
image {
url
}
remark
}
}
`;
function AssessmentList() {
const { loading, error, data } = useQuery(QUERY);
const [ page, setPage ] = useState(1);
const [ search, setSearch ] = useState("");
const getPager = (totalItems, currentPage, pageSize) => {
currentPage = currentPage || 1;
pageSize = pageSize || 25;
var totalPages = Math.ceil(totalItems / pageSize);
var startPage, endPage;
if (totalPages <= 10) {
startPage = 1;
endPage = totalPages;
} else {
if (currentPage <= 6) {
startPage = 1;
endPage = 10;
} else if (currentPage 4 >= totalPages) {
startPage = totalPages - 9;
endPage = totalPages;
} else {
startPage = currentPage - 5;
endPage = currentPage 4;
}
}
var startIndex = (currentPage - 1) * pageSize;
var endIndex = Math.min(startIndex pageSize - 1, totalItems - 1);
var pages = [...Array((endPage 1) - startPage).keys()].map(i => startPage i);
return {
totalItems: totalItems,
currentPage: currentPage,
pageSize: pageSize,
totalPages: totalPages,
startPage: startPage,
endPage: endPage,
startIndex: startIndex,
endIndex: endIndex,
pages: pages
};
}
if (error) return "Error loading assessments";
if (loading) return <h1>Fetching</h1>;
if (data.assessments amp;amp; data.assessments.length) {
const searchQuery = data.assessments.filter((query) => {
return Object.keys(query).some(key => query[key].toString().toLowerCase().includes(search))
});
const pageSize = 24;
const paging = getPager(searchQuery.length, page, pageSize);
const pageOfItems = (searchQuery.slice(paging.startIndex, paging.endIndex 1));
return (
<div>
<input
type="text"
placeholder="Search"
onChange={(e) => setSearch(e.target.value)}
/>
<p></p>
<ul className="image-list">
{pageOfItems.map((res) => (
<li key={res.id} className="image-list__item">
<div className="image__flow thumbnail">
<img src={`${process.env.NEXT_PUBLIC_API_URL}${res.image.url}`} alt="assessment" />
<h4>{res.title}</h4>
<h4>Label</h4>
<h4>{res.label}</h4>
<h5>Assetcode</h5>
<h5>{res.item}</h5>
</div>
</li>
))}
</ul>
<nav aria-label="Page navigation example">
<ul className="pagination" style={{cursor: 'pointer'}}>
<li className={paging.currentPage == 1 ? "page-item disabled" : "page-item"}>
<a className="page-link" onClick={() => setPage(1)}>First</a>
</li>
<li className={paging.currentPage == 1 ? "page-item disabled" : "page-item"}>
<a className="page-link" onClick={() => setPage(paging.currentPage - 1)}>Prev</a>
</li>
{paging.pages.map((page, index) =>
<li key={index} className="page-item">
<a className="page-link" onClick={() => setPage(page)}>{page}</a>
</li>
)}
<li className={paging.currentPage == paging.totalPages ? "page-item disabled" : "page-item"}>
<a className="page-link" onClick={() => setPage(paging.currentPage 1)}>Next</a>
</li>
<li className={paging.currentPage == paging.totalPages ? "page-item disabled" : "page-item"}>
<a className="page-link" onClick={() => setPage(paging.totalPages)}>Last</a>
</li>
</ul>
</nav>
</div>
);
}
}
export default AssessmentList;
Успешно создайте Оценочный Проект!!!
Пожалуйста, ознакомьтесь с моим кодом по адресу: https://github.com/openmymai/nextjs_assessment
Ответ №1:
this
В функциональной составляющей его нет. Вы можете получить доступ к функциям напрямую, без this
использования.
Для того, чтобы получить доступ к реквизиту в крючках. Либо вы можете использовать это,
const Pagination = (props) => {
return (<></>)
}
или вы можете уничтожить реквизит
const Pagination = ({ items, initialPage, onChangePage, pageSize})
Поскольку this
в функциональном компоненте нет привязки, вам необходимо использовать крючки для поддержания состояний и жизненных циклов. Обратитесь к крючкам реакции перед переходом из класса в функциональный компонент, вот руководство по миграции