最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to implement pagination with filter in React - Stack Overflow

programmeradmin6浏览0评论

I have a React app where i use axios to get a list of 200 Todos from a free online REST API with fake data (). My app has one input that you can search/filter for titles of todos and a select/filter that you can choose true or false for the pleted todos. My problem is that i have implement a custom pagination that works well only with the default list of todos, so without filtering the list. For example, if you type in input search the word "vero" you must click in page "2" so you can see existing todo card. Same with select, if you select "true" you can see only 3 cards in the 1st page, but if you click in 2nd page you can see more etc. I have tried a lot but i can't make it work as i want. How can i implement pagination with filter?

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(``)
      .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" && todopleted === true) {
            return todo;
          } else if (filterCompleted === "false" && todopleted === 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: ${todopleted}`}</span>
                  </div>
                </div>
              </div>
            </div>
          );
        })}
    </div>
  );
}

I have a React app where i use axios to get a list of 200 Todos from a free online REST API with fake data (https://jsonplaceholder.typicode./todos). My app has one input that you can search/filter for titles of todos and a select/filter that you can choose true or false for the pleted todos. My problem is that i have implement a custom pagination that works well only with the default list of todos, so without filtering the list. For example, if you type in input search the word "vero" you must click in page "2" so you can see existing todo card. Same with select, if you select "true" you can see only 3 cards in the 1st page, but if you click in 2nd page you can see more etc. I have tried a lot but i can't make it work as i want. How can i implement pagination with filter?

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./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.pleted === true) {
            return todo;
          } else if (filterCompleted === "false" && todo.pleted === 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.pleted}`}</span>
                  </div>
                </div>
              </div>
            </div>
          );
        })}
    </div>
  );
}
Share Improve this question asked Feb 1, 2022 at 19:09 nikostav96nikostav96 2111 gold badge3 silver badges11 bronze badges 1
  • 1 Conceptually, what you'd do is filter FIRST. Store those in a state somewhere. Then use that filtered list as your datasource and page THAT. If no filter, it's just the same data as the unfiltered list. – Nikki9696 Commented Feb 1, 2022 at 19:18
Add a ment  | 

1 Answer 1

Reset to default 5

I fixed pagination with filter with the help of useMemo hook. The problem was that i was filtering the default array and i wasn't updating the totalTodos with the length of puted todos etc.. Now inside useMemo hook, i pute(filter) the todos first and then i update totalTodos with the length of puted todos.

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./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 putedTodos = todos;

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

    if (filterCompleted === "true") {
      putedTodos = putedTodos.filter(
          todo =>
          filterCompleted === "true" && todo.pleted === true
      )
  }

  if (filterCompleted === "false") {
    putedTodos = putedTodos.filter(
        todo =>
        filterCompleted === "false" && todo.pleted === false
    )
  }

    setTotalTodos(putedTodos.length);

    //Current Page slice
    return putedTodos.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.pleted}`}</span>
                  </div>
                </div>
              </div>
            </div>
          );
        })}
    </div>
  );
}
发布评论

评论列表(0)

  1. 暂无评论