I am doing the implementation of list pagination through a custom hook. The handleSetCurrentPage() function gets the correct number, it uses setCurrentPage(number). Consolelog setCurrentPage(number) showed undefined.
if you do all the same code only within one file (put everything in ListOfItems) it works fine.
Hook:
export const usePagination = (users = [], defaultPage = 1, amountPerPage = 10) => {
const [currentPage, setCurrentPage] = useState(defaultPage);
const [currentUsers, setCurrentUsers] = useState([]);
const [amountOfPages, setAmountOfPages] = useState(0);
useEffect(() => {
updateUsers();
updateAmountOfPages();
}, []);
const updateUsers = () => {
const indexOfLastPost = currentPage * amountPerPage;
const indexOfFirstPost = indexOfLastPost - amountPerPage;
const updatedUsers = users.slice(indexOfFirstPost, indexOfLastPost);
setCurrentUsers(updatedUsers);
};
const updateAmountOfPages = () => {
const updatedAmount = Math.ceil(users.length / amountPerPage);
setAmountOfPages(updatedAmount);
};
return {
setCurrentPage,
amountOfPages,
currentUsers,
};
};
list of items:
export function ListOfItems() {
const users = useSelector(state => state);
const { setCurrentPage, currentUsers, amountOfPages } = usePagination(users);
let {url} = useRouteMatch();
let items = currentUsers.map(function (value, index) {
return (
<form key={index}>
<div className="input-group">
<div className="input-group-prepend">
<Link className="input-group-text" to={`${url}/${index}`}>
{value.name}, {index}
</Link>
</div>
</div>
</form>
)
});
return (
<div>
{/*<form className="card">*/}
{/* <Search setSearch={setSearch} />*/}
{/*</form>*/}
<div>{items}</div>
<div>
<Pagination amountOfPages={amountOfPages} setCurrentPage={setCurrentPage}/>
</div>
</div>
)
}
pagination component:
const Pagination = ({amountOfPages, setCurrentPage}) => {
const [pageNumbers, setPageNumbers] = useState([]);
useEffect(() => {
calculatePageNumbers();
}, [amountOfPages]);
function calculatePageNumbers() {
const updatedPageNumbers = [];
for (let i = 1; i <= amountOfPages; i++) {
updatedPageNumbers.push(i);
}
setPageNumbers(updatedPageNumbers);
}
function handleSetCurrentPage(number) {
console.log(number);
return console.log(setCurrentPage(number));
}
return (
<nav>
<ul className="pagination">
{pageNumbers.map(number => (
<li key={number} className="page-item">
<button
onClick={() => handleSetCurrentPage(number)}
type="button"
className="page-link"
>
{number}
</button>
</li>
))}
</ul>
</nav>
);
};
export default Pagination;
useEffect(() => {
updateUsers();
updateAmountOfPages();
}, [currentPage]);
Related
i'm working with React-App and backend/frontend API i get the response everything works fine , but i get too many responses ( over 100 ) at once, how can i go about only getting lets say ( 10 ) at a time, i've tried many things but they dont work. this is my code.
NOT ASKING FOR SOMEONE TO DO THE CODE FOR ME, BUT FOR A LITTLE HELP PUTTING ME ON THE RIGHT DIRECTION
REST API
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react'
const baseUrl = 'http://localhost:3008';
const createRequest = (url) => ({ url });
export const playersAPI = createApi({
reducerPath: 'playersAPI',
baseQuery: fetchBaseQuery({ baseUrl }),
endpoints: (builder) => ({
getplayersAPI: builder.query({
query: (count) => createRequest(`/api/players?limit=${count}`),
}),
})
});
export const { useGetplayersAPIQuery } = playersAPI;
Front Page
import React, { useEffect, useState } from 'react';
import millify from 'millify';
import { Typography, Row, Col, Statistic } from 'antd';
import { Link } from 'react-router-dom';
import { Card } from 'antd';
import { useGetplayersAPIQuery } from '../services/playersAPI';
const { Title } = Typography;
const Players = ({ simplified }) => {
const count = simplified ? 10 : 100;
const { data: playersList, isFetching } = useGetplayersAPIQuery(count);
const [players, setPlayers] = useState();
const [searchTerm, setSearchTerm] = useState('');
console.log(players)
useEffect(() => {
setPlayers(playersList?.players);
const filteredData = playersList?.players.filter((name) => name.name.toLowerCase().includes(searchTerm));
setPlayers(filteredData);
}, [playersList, searchTerm]);
if (isFetching) return 'Loading...';
return (
<>
<div className="search-crypto">
<input placeholder="Search Players" onChange={(e) => setSearchTerm(e.target.value)} />
</div>
<Row gutter={[15, 15]} className="crypto-card-container">
{players?.map((name) => (
<Col xs={24} sm={12} lg={6} className="crypto-card" key={name.id}>
<Link to={`/players/${name.id}`}>
<Card
title={`${name.name}`}
hoverable
>
<p>Name: {(name.name)}</p>
<p>Status: {(name.status)}</p>
<p>Alliancce: {(name.alliance)}</p>
</Card>
</Link>
</Col>
))}
</Row>
</>
)
}
export default Players
This is my front page the squares the ones i want to show only 10 of them at a time, right now it shows all the data from the API.
[https://i.stack.imgur.com/UKHLi.jpg]
If you can't change your backend to add an extra parameter offset, you can do something like this.
const PlayersScreen = () => {
const [data, setData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [playersPerPage] = useState(10);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchPlayers = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await res.json();
setData(data);
setLoading(false)
};
fetchPlayers();
}, []);
// Get current players
const indexOfLastPlayer = currentPage * playersPerPage;
const indexOfFirstPlayer = indexOfLastPlayer - playersPerPage;
const currentPlayers = data.slice(indexOfFirstPlayer, indexOfLastPlayer);
// Change page
const paginate = pageNumber => setCurrentPage(pageNumber);
return (
<div className='container mt-5'>
<h1 className='text-primary mb-3'>My Players</h1>
<Players players={currentPlayers} loading={loading} />
<Pagination
playersPerPage={playersPerPage}
totalPlayers={data.length}
paginate={paginate}
/>
</div>
);
}
const Players = ({ players, loading }) => {
if (loading) {
return <h2>Loading...</h2>;
}
return (
<ul className='list-group mb-4'>
{players.map(player => (
<li key={player.id} className='list-group-item'>
{player.title}
</li>
))}
</ul>
);
}
const Pagination = ({ playersPerPage, totalPlayers, paginate }) => {
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(totalPlayers / playersPerPage); i++) {
pageNumbers.push(i);
}
return (
<nav>
<ul className='pagination'>
{pageNumbers.map(number => (
<li key={number} className='page-item'>
<a onClick={() => paginate(number)} href='!#' className='page-link'>
{number}
</a>
</li>
))}
</ul>
</nav>
);
}
Can someone explain to me why the filter method isn't working as intended? I am trying to update my array of objects with filtered results based on the first name.
export default function App() {
const { data } = useDataFetcher();
const searchArray = data;
const [inputField, setInputField] = useState("");
const searchUser = () => {
searchArray.filter((el) => el.firstName.includes(inputField));
};
useEffect(() => {
searchUser();
}, [inputField]);
return (
<div className="App">
<h1>Xaxis Frontend Interview</h1>
<input onChange={(e) => setInputField(e.target.value)} />
<button onClick={(e) => searchUser()}> Search </button>
{searchArray.map((userData, i) => (
<Users
key={i}
firstName={userData.firstName}
lastName={userData.lastName}
email={userData.email}
bio={userData.bio}
/>
))}
</div>
);
}
You need to create a state which will contain the list of the users. Then, you want to update this state when inputField state changes.
export default function App() {
const { data } = useDataFetcher();
const searchArray = data;
const [users, setUsers] = useState([])
const [inputField, setInputField] = useState("");
useEffect(() => {
setUsers(data);
}, [data]);
useEffect(() => {
const filteredArray = users.filter((el) => el.firstName.includes(inputField));
setUsers(filteredArray)
}, [inputField]);
return (
<div className="App">
<h1>Xaxis Frontend Interview</h1>
<input onChange={(e) => setInputField(e.target.value)} />
{users.map((userData, i) => (
<Users
key={i}
firstName={userData.firstName}
lastName={userData.lastName}
email={userData.email}
bio={userData.bio}
/>
))}
</div>
);
}
Or if you want to update the list of users by clicking a button:
export default function App() {
const { data } = useDataFetcher();
const searchArray = data;
const [users, setUsers] = useState([])
const [inputField, setInputField] = useState("");
useEffect(() => {
setUsers(data);
}, [data]);
const searchUser = () => {
const filteredArray = users.filter((el) => el.firstName.includes(inputField));
setUsers(filteredArray)
};
return (
<div className="App">
<h1>Xaxis Frontend Interview</h1>
<input onChange={(e) => setInputField(e.target.value)} />
<button onClick={() => searchUser()}> Search </button>
{users.map((userData, i) => (
<Users
key={i}
firstName={userData.firstName}
lastName={userData.lastName}
email={userData.email}
bio={userData.bio}
/>
))}
</div>
);
}
when I added eventList into my useEffect in an array like this:
const getEvents = async () => {
const res = await axios.get(`/api/v1/events/event/${teamId}`);
setEventList(res.data.Events);
};
useEffect(() => {
getEvents();
}, [eventList]);
It just keep fetching data non stop
but if I don't put eventList in there my page just fetch it one time and when I click to another component is will gone
Added Component:
Calendar.js:
const Calendar = (props) => {
const { teamId } = props;
const events = [];
const [date, setDate] = useState(new Date());
const [eventList, setEventList] = useState([]);
const [expanded, setExpanded] = useState("");
const getEvents = async () => {
const res = await axios.get(`/api/v1/events/event/${teamId}`);
setEventList(res.data.Events);
};
useEffect(() => {
getEvents();
}, []);
const handleChangeAccordion = (panel) => (event, newExpanded) => {
setExpanded(newExpanded ? panel : false);
};
const handleChangeCalendar = (value) => {
const currentDate = moment(value).format("YYYY-MM-DD");
setDate(currentDate);
const currentEvents = events.find((event) =>
moment(currentDate).isSame(event.date, "day")
);
setEventList(currentEvents ? currentEvents.events : []);
};
const [showCalendarCard, setShowCalendarCard] = useState(false);
const addEvent = () => {
setShowCalendarCard(true);
};
return (
<div className="calendar-tab">
<div className="event-view-container">
<div className="event-date">
<p className="event-date-monthday">{moment(date).format("D")}</p>
<p className="event-date-weekday">{moment(date).format("dddd")}</p>
</div>
<div className="event-list">
{eventList.map((event, index) => (
<div>
<Accordion
key={`event-${index}`}
square
expanded={expanded === `event${index + 1}`}
onChange={handleChangeAccordion(`event${index + 1}`)}
>
<AccordionSummary>
<div className="event-list-item-header">
<span className="timestart">
{moment(event.timestart, "HH:mm:ss").format("h:mm A")}
</span>
<span className="dash">-</span>
<span className="timeend">
{moment(event.timeend, "HH:mm:ss").format("h:mm A")}
</span>
<span className="title">{event.title}</span>
</div>
</AccordionSummary>
</Accordion>
<div className="event-list-item-content">
<div className="header">
<span className="announcements">Announcements</span>
<div className="plus">
<ControlPoint />
</div>
</div>
<div className="content">{event.description}</div>
</div>
</div>
))}
</div>
</div>
<div className="calendar-view-container">
<div className="event-calendar-container">
{!showCalendarCard ? (
<div>
<EventCalendar
className="event-calendar"
formatShortWeekday={(locale, date) =>
moment(date).format("dd").charAt(0)
}
tileClassName={({ date }) => {
if (events.find((x) => moment(x.date).isSame(date, "day"))) {
return "highlight";
}
}}
onChange={(value) => handleChangeCalendar(value)}
nextLabel={<NavigateNext />}
prevLabel={<NavigateBefore />}
/>
<div className="add-event">
<ControlPoint onClick={addEvent} />
</div>
</div>
) : (
<CalendarCard
setShowCalendarCard={setShowCalendarCard}
teamId={teamId}
/>
)}
</div>
</div>
</div>
);
};
export default Calendar;
When I click to another day in calendar it will disspear my list of events.
Here is the photo of my project:
Your useEffect is depending on eventList to change and by calling that function you are changing eventList, If you want to send the request only once then this solution will do
const Calendar = (props) => {
const { teamId } = props;
// const events = [];
const [date, setDate] = useState(new Date());
const [eventList, setEventList] = useState([]);
const [expanded, setExpanded] = useState("");
const getEvents = async () => {
const res = await axios.get(`/api/v1/events/event/${teamId}`);
setEventList(res.data.Events);
};
useEffect(() => {
getEvents();
}, []);
Having an empty dependant list will only trigger the function once so change
useEffect(() => {
getEvents();
}, [eventList]);
To this
useEffect(() => {
getEvents();
}, []);
Its stuck in a loop because your useEffect callback has the side-effect changing it's own dependency (eventList).
Basically instead of resetting the event list you fetched from the API, memoize the filtered list
e.g.
const filteredEventList = useMemo(() => {
return eventList.filter((event) =>
moment(date).isSame(event.date, "day")
)
}, [date, eventList])
and then use this filtered event list in your render
I want to add a filter in arrays of filters, but all filters are added in the same array in filters. There are some blocks for filters and every filter must be added in its array. Now, every filter is added in its array, but all other filters are updated in that array too.
export const DropDownBlock = () => {
const [filters, setFilters] = useState({
type: [],
license: [],
tag: [],
format: [],
});
const filterKey = Object.keys(item.filters);
const [checked, setChecked] = useState([]);
return (
<section className="filterSection">
{filterKey.map((f, index) => {
const filterArray = [];
const photoItems = photos.map((p) => {
return p.filters[filterKey[index]];
});
photoItems.map((p) => {
if (filterArray.indexOf(p) < 0) {
filterArray.push(p);
}
});
const handleFilters = (filters, category) => {
const newFilters = { ...filters };
newFilters[category] = filters;
setFilters(newFilters);
};
return (
<div className="" key={f}>
<div
className="dropDownTitleBlock"
onClick={() => (isOpen ? setIsOpen(false) : setIsOpen(true))}
>
{isOpen ? <MdKeyboardArrowDown /> : <MdKeyboardArrowRight />}
<h5 className="dropDownTitle">{f}</h5>
</div>
{isOpen && (
<div className="dropDownCategoryBlock">
{filterArray.map((filter) => {
switch (f) {
case filterKey[index]:
return (
<Checkbox
filter={filter}
handleFilters={(filters) =>
handleFilters(filters, filterKey[index])
}
checked={checked}
setChecked={setChecked}
/>
);
}
})}
</div>
)}
</div>
);
})}
</section>
);
};
Using React.js, When I change between filters buttons, I want pagination get back to the first page (number one).
This is my code in pagination component:
import React, { useState } from 'react'
const Pagination = ({productsPerPage, totalPosts, paginate}) => {
const [currentPage, setCurrentPage] = useState(1)
const PageNumbers =[]
const int = Math.ceil(totalPosts / productsPerPage)
for (let i = 1; i<= int; i++) {
PageNumbers.push(i)
}
return (
<nav className="">
<ul className="pagination">
<li className={currentPage === 1 ? 'disabled' : ''}>
<a onClick={() =>{setCurrentPage(currentPage - 1); paginate(currentPage - 1);}}>Previous</a>
</li>
{PageNumbers.map(number => (
<li
key={number}
className={number === currentPage ? "page-item active" : "page-item "}
>
<a
onClick={() => paginate(number)}
href="!#"
className="page-link "
>
{number}
</a>
</li>
))}
<li className={currentPage === int ? 'disabled' : ''}>
<a onClick={() => {setCurrentPage(currentPage + 1); paginate(currentPage + 1); }}>Next</a>
</li>
</ul>
</nav>
)
}
export default Pagination
This is the main App
const App = () => {
const [itemsAmount] = useState(100);
const [fromProduct, setFromProduct] = useState(1);
const [keys, setKeys] = useState([]);
const [products, setProducts] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [productsPerPage, setProductsPerPage] = useState(10);
useEffect(() => {
axios('The API link')
.then(res => {
setProducts(res.data.itemsList);
res.data.itemsList[0] &&
setKeys(Object.keys(res.data.itemsList[0]).map((key, index) => key));
})
}, [Organization, HardWareStatuses, hardvarutyp, fromProduct,
itemsAmount,setProductsPerPage]);
/* Get current products*/
const indexOfLastProduct = currentPage * productsPerPage;
const indexOfFirstProduct = indexOfLastProduct - productsPerPage;
const currentProducts =
products.slice(indexOfFirstProduct,indexOfLastProduct );
/* Change page */
const paginate = pageNumber => setCurrentPage(pageNumber)
return (
<div>
{/* Create pagination */}
<div className="">
<Pagination
productsPerPage={productsPerPage}
totalProducts={products.length}
paginate={paginate}
filters ={filters}
/>
</div>
</div>
<Products products={currentProducts} keys={keys} />
<ExportCSV products={products} keys={keys} />
</div>
);
};
export default App;
Your pagination component need to be aware about your filters, and I guess those are managed by a parent component. One easy way would be to add them as parameters to your component to make it aware of them.
<Pagination productsPerPage="productsPerPage" totalPosts="totalPosts" paginate="paginate" filters="filters"/>
This way, you can implement a listenner to run some code only when filters change, using the useEffect hook:
import React, { useState, useEffect } from 'react'
const Pagination = ({productsPerPage, totalPosts, paginate, filters}) => {
const [currentPage, setCurrentPage] = useState(1)
const PageNumbers =[]
const int = Math.ceil(totalPosts / productsPerPage)
useEffect(() => {
setCurrentPage(1);
}, [filters]);
for (let i = 1; i<= int; i++) {
PageNumbers.push(i)
}
return (
<nav className="">
...
</nav>
)
}
export default Pagination;