如何在 React 中使用过滤器实现分页
How to implement pagination with filter in React
我有一个 React 应用程序,我在其中使用 axios 从免费的在线 REST API 中获取包含 200 个待办事项的列表,其中包含虚假数据 (https://jsonplaceholder.typicode.com/todos)。我的应用程序有一个输入,您可以 search/filter 作为待办事项的标题,还有一个 select/filter ,您可以为已完成的待办事项选择 true 或 false。我的问题是我已经实现了一个自定义分页,它只适用于默认的待办事项列表,所以没有过滤列表。例如,如果您在输入搜索中输入单词“vero”,您必须在页面“2”中单击,这样您才能看到现有的待办事项卡片。与 select 相同,如果你 select "true" 你只能在第一页看到 3 张卡片,但如果你点击第二页你可以看到更多等等。我已经尝试了很多但我可以'让它按照我的意愿工作。如何使用过滤器实现分页?
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 挂钩的帮助下修复了过滤器的分页。问题是我正在过滤默认数组并且我没有用计算的 todos 等的长度更新 totalTodos。现在在 useMemo 挂钩中,我首先计算(过滤)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>
);
}
我有一个 React 应用程序,我在其中使用 axios 从免费的在线 REST API 中获取包含 200 个待办事项的列表,其中包含虚假数据 (https://jsonplaceholder.typicode.com/todos)。我的应用程序有一个输入,您可以 search/filter 作为待办事项的标题,还有一个 select/filter ,您可以为已完成的待办事项选择 true 或 false。我的问题是我已经实现了一个自定义分页,它只适用于默认的待办事项列表,所以没有过滤列表。例如,如果您在输入搜索中输入单词“vero”,您必须在页面“2”中单击,这样您才能看到现有的待办事项卡片。与 select 相同,如果你 select "true" 你只能在第一页看到 3 张卡片,但如果你点击第二页你可以看到更多等等。我已经尝试了很多但我可以'让它按照我的意愿工作。如何使用过滤器实现分页?
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 挂钩的帮助下修复了过滤器的分页。问题是我正在过滤默认数组并且我没有用计算的 todos 等的长度更新 totalTodos。现在在 useMemo 挂钩中,我首先计算(过滤)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>
);
}