Крюк ReactJS — я пытаюсь преобразовать из разбиения на страницы componentWillMount и componentDidUpdate в крюк React, но не получается

#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 в функциональном компоненте нет привязки, вам необходимо использовать крючки для поддержания состояний и жизненных циклов. Обратитесь к крючкам реакции перед переходом из класса в функциональный компонент, вот руководство по миграции