javascript - 如何在 React 中使用过滤器实现分页

标签 javascript reactjs filter pagination crud

我有一个 React 应用程序,我在其中使用 axios 从免费的在线 REST API 中使用虚假数据 (https://jsonplaceholder.typicode.com/todos) 获取 200 个待办事项列表。我的应用程序有一个输入,您可以搜索/过滤待办事项的标题,还有一个选择/过滤器,您可以为已完成的待办事项选择真或假。我的问题是我已经实现了一个自定义分页,它只适用于默认的待办事项列表,所以没有过滤列表。例如,如果您在输入搜索中输入单词“vero”,您必须在页面“2”中单击,这样您才能看到现有的待办事项卡片。与选择相同,如果您选择“true”,您在第一页中只能看到 3 张卡片,但如果您在第二页中单击,您可以看到更多卡片等等。我已经尝试了很多,但我无法按我想要的方式工作.如何使用过滤器实现分页?

Edit React list with filtering and pagination

App.js

import { useState, useEffect } from "react";
import axios from "axios";
import "./styles.css";

export default function App() {
  const [todos, setTodos] = useState([]);
  const [searchTerm, setSearchTerm] = useState("");
  const [filterCompleted, setFilterCompleted] = useState("");

  const [currentPage, setCurrentPage] = useState(1);
  const [todosPerPage, setTodosPerPage] = useState(10);

  // Get current todos
  const indexOfLastTodo = currentPage * todosPerPage;
  const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
  const currentTodos = todos.slice(indexOfFirstTodo, indexOfLastTodo);

  const totalTodos = todos.length;
  const pageNumbers = [];

  for (let i = 1; i <= Math.ceil(totalTodos / todosPerPage); i++) {
    pageNumbers.push(i);
  }

  // Change page
  const paginate = (pageNumber) => setCurrentPage(pageNumber);

  useEffect(() => {
    axios
      .get(`https://jsonplaceholder.typicode.com/todos`)
      .then((response) => {
        setTodos(response.data);
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);

  const resetFilter = () => {
    setSearchTerm("");
    setFilterCompleted("");
  };

  return (
    <div className="container">
      <h3>Filters</h3>
      <div className="mb-3">
        <label htmlFor="search" className="form-label">
          Search
        </label>
        <input
          type="text"
          className="form-control"
          id="search"
          placeholder="Search Title"
          value={searchTerm}
          onChange={(e) => {
            setSearchTerm(e.target.value);
          }}
        />
      </div>
      <div className="mb-3">
        <label htmlFor="search" className="form-label">
          Completed
        </label>
        <select
          className="form-select"
          value={filterCompleted}
          onChange={(e) => {
            setFilterCompleted(e.target.value);
          }}
        >
          <option defaultValue=""></option>
          <option value="true">true</option>
          <option value="false">false</option>
        </select>
      </div>
      <div className="mb-3">
        <button
          type="button"
          className="btn btn-danger btn-sm"
          onClick={resetFilter}
        >
          Reset Filters
        </button>
      </div>

      <nav>
        <ul className="pagination">
          {pageNumbers.map((number) => (
            <li key={number} className="page-item">
              <button onClick={() => paginate(number)} className="page-link">
                {number}
              </button>
            </li>
          ))}
        </ul>
      </nav>

      {currentTodos
        .filter((todo) => {
          if (searchTerm === "") {
            return todo;
          } else if (
            todo.title.toLowerCase().includes(searchTerm.toLowerCase())
          ) {
            return todo;
          }
        })
        .filter((todo) => {
          if (filterCompleted === "") {
            return todo;
          } else if (filterCompleted === "true" && todo.completed === true) {
            return todo;
          } else if (filterCompleted === "false" && todo.completed === false) {
            return todo;
          }
        })
        .map((todo) => {
          return (
            <div key={todo.id} className="card margin-bottom">
              <h5 className="card-header">
                <div className="card-header-flex">
                  <span className="id">{`#${todo.id}`}</span>
                </div>
              </h5>
              <div className="card-body">
                <div className="card-text">
                  <div className="card-body-flex">
                    <span>{`Title: ${todo.title}`}</span>
                    <span>{`Completed: ${todo.completed}`}</span>
                  </div>
                </div>
              </div>
            </div>
          );
        })}
    </div>
  );
}

最佳答案

我在 useMemo 钩子(Hook)的帮助下用过滤器修复了分页。问题是我正在过滤默认数组并且我没有用计算的 todos 等的长度更新 totalTodos。现在在 useMemo Hook 中,我首先计算(过滤)todos 然后我用计算的长度更新 totalTodos待办事项。

import { useState, useEffect, useMemo } from "react";
import axios from "axios";
import "./styles.css";

export default function App() {
  const [todos, setTodos] = useState([]);
  const [searchTerm, setSearchTerm] = useState("");
  const [filterCompleted, setFilterCompleted] = useState("");

  const [currentPage, setCurrentPage] = useState(1);
  const [totalTodos, setTotalTodos] = useState(0);
  const todosPerPage = 10;

  useEffect(() => {
    axios
      .get(`https://jsonplaceholder.typicode.com/todos`)
      .then((response) => {
        setTodos(response.data);
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);

  
  const pageNumbers = [];

  for (let i = 1; i <= Math.ceil(totalTodos / todosPerPage); i++) {
    pageNumbers.push(i);
  }


  const todosData = useMemo(() => {
    let computedTodos = todos;

    if (searchTerm) {
        computedTodos = computedTodos.filter(
            todo =>
            todo.title.toLowerCase().includes(searchTerm.toLowerCase())
        );
    }

    if (filterCompleted === "true") {
      computedTodos = computedTodos.filter(
          todo =>
          filterCompleted === "true" && todo.completed === true
      )
  }

  if (filterCompleted === "false") {
    computedTodos = computedTodos.filter(
        todo =>
        filterCompleted === "false" && todo.completed === false
    )
  }

    setTotalTodos(computedTodos.length);

    //Current Page slice
    return computedTodos.slice(
        (currentPage - 1) * todosPerPage,
        (currentPage - 1) * todosPerPage + todosPerPage
    );
}, [todos, currentPage, searchTerm, filterCompleted]);
  // Change page
  const paginate = (pageNumber) => setCurrentPage(pageNumber);

  const resetFilter = () => {
    setSearchTerm("");
    setFilterCompleted("");
    setCurrentPage(1);
  };

  return (
    <div className="container">
      <h3>Filters</h3>
      <div className="mb-3">
        <label htmlFor="search" className="form-label">
          Search
        </label>
        <input
          type="text"
          className="form-control"
          id="search"
          placeholder="Search Title"
          value={searchTerm}
          onChange={(e) => {
            setSearchTerm(e.target.value);
            setCurrentPage(1);
          }}
        />
      </div>
      <div className="mb-3">
        <label htmlFor="search" className="form-label">
          Completed
        </label>
        <select
          className="form-select"
          value={filterCompleted}
          onChange={(e) => {
            setFilterCompleted(e.target.value);
            setCurrentPage(1);
          }}
        >
          <option defaultValue=""></option>
          <option value="true">true</option>
          <option value="false">false</option>
        </select>
      </div>
      <div className="mb-3">
        <button
          type="button"
          className="btn btn-danger btn-sm"
          onClick={resetFilter}
        >
          Reset Filters
        </button>
      </div>

      <nav>
        <ul className="pagination">
          {pageNumbers.map((number) => (
            <li key={number} className="page-item">
              <button onClick={() => paginate(number)} className="page-link">
                {number}
              </button>
            </li>
          ))}
        </ul>
      </nav>

      {todosData
        .map((todo) => {
          return (
            <div key={todo.id} className="card margin-bottom">
              <h5 className="card-header">
                <div className="card-header-flex">
                  <span className="id">{`#${todo.id}`}</span>
                </div>
              </h5>
              <div className="card-body">
                <div className="card-text">
                  <div className="card-body-flex">
                    <span>{`Title: ${todo.title}`}</span>
                    <span>{`Completed: ${todo.completed}`}</span>
                  </div>
                </div>
              </div>
            </div>
          );
        })}
    </div>
  );
}

关于javascript - 如何在 React 中使用过滤器实现分页,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70946170/

相关文章:

javascript - 类型错误 : f is undefined

javascript - jQuery:是否可以在点击时更改 ID/无类元素?

javascript - 使卡片和图像大小相同——我如何使用 CSS 做到这一点?

javascript - 为什么react-native中默认 View 宽度是100%?为什么使用alignItems时没有默认宽度?

reactjs - react 中的传播运算符抛出意外 token 的错误

javascript - 在 jQuery 终端中的同一行上回显内容

javascript - Jquery Mobile Filter系统,无法隐藏多个div

python - 在 Django 中是否有内置的方法来过滤给定的函数?

android - 带有回收器 View 的搜索 View 无法正常工作

javascript - php根据条件合并数组