I'm trying to build a re-usable component to be used for pagination.
Before making you guys read more, this is how my state looks like:
I'm calling it from a component where I'm fetching post from MongoDB.
From there I'm having troubles with useEffect to make the changes and with my Pagination component that I'm not even sure if I'm building it properly.
This is my useEffect:
const params = new URLSearchParams(window.location.search);
const page = parseInt(params.get('page')) || 1;
const limit = parseInt(params.get('limit')) || 1;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
// const currentPosts = posts.slice(endIndex, startIndex);
const currentPosts = Object.entries(posts).slice(endIndex, startIndex);
console.log(currentPosts);
useEffect(() => {
getPosts(null, null, page, limit);
getCurrentProfile();
}, [getPosts, getCurrentProfile]);
// Change page
const paginate = pageNumber => page(pageNumber);
this is the component I'm calling inside the loop that is fetching the posts.
<Pagination
limit={limit}
totalPosts={posts.data.length}
paginate={paginate}
/>
Finally this is how I'm working with the component, it is obviously still a work in progress.
import React from 'react';
const setPagination = ({ limit, totalPosts, paginate }) => {
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(totalPosts / limit); i++) {
pageNumbers.push(i);
}
return (
<nav>
<ul className='pagination'>
{pageNumbers.map(number => (
<li key={number} className='page-item'>
<a
onClick={() => paginate(number)}
href={`?page=${number}&limit=${limit}`}
className='page-link'
>
{number}
</a>
</li>
))}
</ul>
</nav>
);
};
export default setPagination;
Right now, I'm trying to make it work with query strings from my url:
http://localhost:3000/posts?page=1&limit=1
Can anyone guide me on the logic that I'm missing? I'm having a headache with this xD.
EDIT:Sorry I did not make it clear. This is the problem:
Should not it create more pages?
Related
so I am trying to build an amazon clone to learn nextjs. I am trying to use react-context to save the chosen product's id number to an array. the data is being saved and I can access it from any of the components in the project but whenever a product with an id that has 2 digits the array.length increases by 2. here is my code
`
import React, { createContext, useContext, useState } from 'react';
const AppContext = createContext();
export function AppWrapper({ children }) {
var [basket, addToBasket]= useState([]);
return (
<AppContext.Provider value={[basket, addToBasket]}>
{children}
</AppContext.Provider>
);
}
export function useAppContext() {
return useContext(AppContext);
}
function Product({id, title, price, description, category, image }) {
var [basket, addToBasket] = useAppContext();
const addItemToBasket = () => {
addToBasket(basket + id);
}
return(
<button onClick={addItemToBasket} className='button'>Add to Basket</button>
<h1>items ID in basket: {basket}</h1>
<h1>length of array: {basket.length}</h1>
)
I did try this and I couldn't get it to work:
let counter = 0;
const addItemToBasket = () => {
for (let i = 0; i < basket.length; i++) {
if (basket[i].status === '0') counter++;
};
addToBasket(basket + id);
}
...
<h1>length of array: {counter}</h1>
I am pretty new to javascript so I did a lot of different variations of this. I gave the for loop its own function, I kept it out on its own but nothing I tried could get it to work. thanks for the help y'all.
useState returns a getter and a setter, not a getter and a pusher. Your addToBasket sets the value of the basket property to (basket + id), which will be a string. More specifically, basket is originally an array, but + on any arguments that are non-numeric results in a string; once basket is a string, basket + id will grow the string by the number of characters in the string representation of id.
To use the setter correctly:
const [basket, setBasket] = useState([]);
const addItemToBasket = () => {
setBasket([...basket, basket + id]);
}
I am building an animation where the letters of two words appear one by one, similar to a slide-in effect. I have the code made with jQuery, but I need to implement it in my React app (built with hooks). The code that I have takes the text, splits it creating individual letters, and adds spans between those letters. This is the following code that I need to convert to React:
const logoText = document.querySelector('.logo');
const stringText = logoText.textContent;
const splitText = stringText.split("");
for (let i=0; i < splitText.length; i++) {
text.innerHTML += "<span>" + splitText + "</span>"
}
let char = 0;
let timer = setInterval(onTick, 50)
I was wondering if you guys could help me figure it out. Thanks a lot!
You need to iterate over the text and create a timeout function for every letter with a different time of execution, that way will be visible the slide effect you are expecting:
Custom hook
const useSlideInText = text => {
const [slide, setSlide] = useState([]);
useEffect(() => {
Array.from(text).forEach((char, index) => {
const timeout = setTimeout(
() =>
setSlide(prev => (
<>
{prev}
<span>{char}</span>
</>
)),
index * 100
);
});
}, []);
return slide;
};
Usage
function App() {
const slide = useSlideInText("hello");
return (
<div>
{slide}
</div>
);
}
Working example
I am assuming the React components that you want to run this hook in possess the text you want to split. I am also assuming that on the interval, you want to reveal more of the text. In that case my example solution would look like this:
Hook
import {useState, useEffect} from "react";
const useSlideInText = (text) => {
const [revealed, setRevealed] = useState(0);
useEffect(() => {
if (revealed < text.length) {
setTimeout(() => setRevealed(revealed + 1), 50);
}
});
return text.split('').slice(0, revealed).map((char) => (<span>{char}</span>));
}
Example usage
const MyComponent = (props) => {
const displayText = useSlideInText(props.text);
return <div>{displayText}</div>;
};
going off of the other answer:
const generateDisplayTest = (text, numChars) => text.split('').slice(0, numChars).map((char) => (<span>{char}</span>));
const MyComponent = (props) => {
const [revealed, setRevealed] = useState(0);
useEffect(() => {
if (revealed < props.text.length) {
setTimeout(() => setRevealed(revealed + 1), 50);
}
}, [revealed]);
const displayText = generateDisplayTest(props.text, revealed);
return <div>{displayText}</div>;
};
including [revealed] in the useEffect means that useEffect will run every time that revealed changes. Also I always feel that useState/useEffect should live on the component, it has been that way in the place I worked but I'm not sure if that is industry standard.
I am new to React and am trying to create my own scrollbar. I am using a local JSON API to simulate getting data which is then listing the data as 'cards'. I built a couple of other components to organize and handle the information, it looks like this:
Scrollbar (Buttons to Navigate)
|-->CardList (Handles iterating over the cards)
|-->Cards (Template for displaying an individual card)
The issue I am having is that when I trigger the Button event handleNext it will successfully update offset & limit and pass these to CardList. However, it does not reiterate over the array and output the next 5 items. Instead it removes all the Cards from CardList and I am left with an empty screen.
Scrollbar.js:
const { data isPending, error} = useFetch ('http://localhost:8000/data');
const [limit, setLimit] = useState(5);
const [offset, setOffset] = useState(0);
const handleNext = () => {
setOffset(offset + limit);
console.log("Offset: " +offset);
}
return (
<div className="scrollbar">
{error && <div>{error}</div>}
{isPending && <div>Loading...</div>}
{data && <CardList data={data} offset={offset} limit={limit}/> }
<Button onClick={handleNext}/>
</div>
);
}
export default Scrollbar;
CardList.js:
const CardList = ({data , offset, limit}) => {
return (
<div className="card-list" >
{data.slice(offset,limit).map((data) => (
<Card data={data} key={data.id}/>
))}
</div>
);
}
export default CardList;
The problem is when handleNext is triggered offset is going to be equal to 5 while limit keeps the value of 5 too, then when u do slice(offset,limit) is going to be replaced by slice(5,5) that returns an []. What u want is increasing the limit too if u increase the offset, for example:
const handleNext = () => {
setOffset(offset + limit);
setLimit(limit + limit)
}
I'm trying to build a component where an array of values are presented like a slider, where every time, the old content gets replaced by a new one, and that can be done using the buttons next and previous. The component is working, but I'm struggling a little bit in the edge cases, where I have to disable the buttons
I'll leave the link to a codesandbox where the component is being built, I'm sure it'll be easier to understand what's going on.
Link to sandbox
Try not to use state value for tracking button disabled status. Please check below.
import React, { useState } from "react";
export default function App() {
const data = ["q", "c", "s", "a"];
const [iterator, setIterator] = useState(0);
const [curr, setCurr] = useState(data[iterator]);
const fetchNext = () => {
if (iterator === data.length - 1) {
return;
}
setIterator((prev) => prev + 1);
setCurr(data[iterator + 1]);
};
const fetchPrevious = () => {
if (iterator === 0) {
return;
}
setIterator((prev) => prev - 1);
setCurr(data[iterator - 1]);
};
const nextDisabled = iterator >= data.length - 1;
const prevDisabled = iterator <= 0;
return (
<div>
<h1>{curr}</h1>
<button disabled={nextDisabled} onClick={fetchNext}>
next
</button>
<button disabled={prevDisabled} onClick={fetchPrevious}>
previous
</button>
</div>
);
}
Use react.js. Catching hard to understanding error. My component without pagination work well - show you all items and you can see the item by click. Pagination work fine too, but i cant click on item in item list. Actualy i can click, but displaying only first page items. If you click on item from 2-nd(3,4...n) page you get item from 1-st page.
Open CodePen with my code
export function ListOfItems() {
const [currentPage, setCurrentPage] = useState(1);
const [postsPerPage] = useState(10);
const users = useSelector(state => state);
const indexOfLastPost = currentPage * postsPerPage;
const indexOfFirstPost = indexOfLastPost - postsPerPage;
const currentPosts = users.slice(indexOfFirstPost, indexOfLastPost);
const paginate = pageNumber => setCurrentPage(pageNumber);
let items = currentPosts.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}
</Link>
</div>
</div>
</form>
)
});
return (
<div>
<div>{items}</div>
<Pagination postsPerPage={postsPerPage} totalUsers={users.length} paginate={paginate}/>
</div>
)
}
Recently I've built something like you.
There is a more clean way to do it.
I recommend you to separate your logic in custom hooks.
For example, you can create custom hook:
export const usePagination = (posts, defaultPage = 1, amountPerPage = 10) => {
const [currentPage, setCurrentPage] = useState(defaultPage);
const [postsPerPage] = useState(amountPerPage);
const indexOfLastPost = currentPage * postsPerPage;
const indexOfFirstPost = indexOfLastPost - postsPerPage;
let currentPosts = [];
let amountOfPages = 0;
if (Array.isArray(posts)) {
currentPosts = posts.slice(indexOfFirstPost, indexOfLastPost);
amountOfPages = Math.ceil(posts.length / postsPerPage);
}
return {
setCurrentPage,
amountOfPages,
currentPosts,
};
};
And use it in any component you need. For example:
const { setCurrentPage, currentPosts, amountOfPages } = usePagination(yourArrayOfData);
And for example you can use it that way(I was using Material UI Pagination component):
<Pagination
count={amountOfPages}
onChange={(event, page) => setCurrentPage(page)}
/>
And use currentPosts for actually displaying your data.
I know, that it's not direct answer to your question, but recently I have written something like you and it worked perfectly. So I hope that my solution will help you.