Handle click on pagination link - javascript

I have this code and I want that after click li console.log display number of page. So I count my pages in pages const. I tryed do that on two way.
First:
const handlePageClick = (i) => {
console.log(i);
}
const Pagination = ({pages}) => {
let list = []
for(let i = 1; i <= pages; i++){
list.push(<li key={i} onClick={handlePageClick(i)}>{i}</li>)
}
return list;
}
return (
<React.Fragment>
<div className="row">
<ul>
<Pagination pages={pages} />
</ul>
</div>
</React.Fragment>
);
It run handlePageClick method after run the app, not after click.
Second method I tryed:
const linkRef = useRef(null);
const handlePageClick = (i) => {
console.log(linkRef.current.innerText);
}
const Pagination = ({pages}) => {
let list = []
for(let i = 1; i <= pages; i++){
list.push(<li key={i} ref={linkRef} onClick={handlePageClick}>{i}</li>)
}
return list;
}
It display the last result all the times. How can I solve my problem?

You have to pass a function to your onClick handler, so you can call handlePageClick in that function, and pass your current value of i like this:
const Pagination = ({pages}) => {
let list = []
for(let i = 1; i <= pages; i++){
list.push(<li key={i} ref={linkRef} onClick={() => handlePageClick(i)}>{i}</li>)
}
return list;
}
Notice how you're now passing () => handlePageClick(i) is now what you're passing to onClick. This is a function that gets executed when the click event happens, and it passes your current value of i

In your first attempt you need to use onClick={() => handlePageClick(i)}. This is because currently it is invoked on the app render because you are invoking the method so you create an infinite loop where the app is rendered -> the onClick event is invoked which causes a another render. Using an arrow function means you pass a function which is why you invoke it.

Related

for loop wont work in a function inside react component

import React from 'react';
const RowArray=()=>{
return(
<div>
<h1>Row Array</h1>
</div>
)
};
const chunk_array = (list, integer)=>{
let temp_arr = list;
console.log('chunks',list,'integer',integer);
const list_of_chunks = [];
const iteration = Math.ceil(+list.length/+integer);
// list.map(x => {console.log(x,"map")})
for (let i;i< iteration ;i++ ){
console.log(i);
let temp_chunk = temp_arr.splice(6, temp_arr.length);
list_of_chunks.push(temp_chunk);
};
return list_of_chunks;
}
const TableArray=({details})=>{
const data = chunk_array(details);
console.log('data', data);
return(
<div className="d-flex flex-row">
<RowArray/>
</div>
)
};
export default TableArray;
the for loop in function chunk array won't work, supported as no i was logged in the console. I understand in jsx for loop may not work, I believe I define the function in pure javascript enviroment, so why do you think it is?
Console.log(i) doesn't log anything, as in the function skipped for loop line
you haven't initialized the value of i in the for loop
for (let i = 0; i < iteration; i++) {
// your code
}
chunk_array function expects two arguments and you're only passing one argument details

How to set state with a setInterval() when a button is clicked?

I'm trying to update the count of activeIndex within the setInterval when the checkbox is ticked. handleNext() and handlePrevious() are working fine when buttons are clicked but when the checkbox is checked the value of activeIndex is not getting updated in handleNext() but it's getting updated on the screen, so there is no condition check for activeIndex and it goes beyond 3.
import { useState } from "react";
import "./styles.css";
export default function App() {
const [slideTimer, setSlideTimer] = useState(null);
const [activeIndex, setActiveIndex] = useState(0);
const slideDuration = 1000;
const handleNext = () => {
if ((activeIndex) >= 3) {
setActiveIndex(0);
} else {
setActiveIndex(prev => prev + 1);
}
};
const handlePrev = () => {
if (activeIndex <= 0) {
setActiveIndex(3);
} else {
setActiveIndex((prev) => prev - 1);
}
};
const toggleSlider = () => {
if (slideTimer) {
clearInterval(slideTimer);
setSlideTimer(null);
return;
}
setSlideTimer(setInterval(handleNext, slideDuration));
};
return (
<div className="App">
<h1>{activeIndex}</h1>
<button onClick={() => handleNext()}>Next</button>
<button onClick={() => handlePrev()}>Previous</button>
<input onClick={() => toggleSlider()} type="checkbox" />
</div>
);
}
I have tried putting the code for toggleSlider inside useEffect() but that too is not working.
Your problem is that when handleNext gets defined (which occurs on every rerender), it only knows about the variables/state in its surrounding scope at the time that it's defined. As a result, when you queue an interval with:
setInterval(handleNext, slideDuration)
You will be executing the handleNext function that only knows about the component's state at that time. When your component eventually rerenders and sets a new value for activeIndex, your interval will still be exeucting the "old" handleNext function defined in the previous render that doesn't know about the newly updated state. One option to resolve this issue is to make the hanldeNext function not rely on state obtained from its outer scope, but instead, use the state setter function to get the current value:
const handleNext = () => {
setActiveIndex(currIndex => (currIndex + 1) % 4);
};
Above I've used % to cycle the index back to 0, but you very well can also use your if-statement, where you return 0 if currIndex >= 3, or return currIndex + 1 in your else.
I would also recommend that you remove the slideTimer. This state value isn't being used to describe your UI (you can see that as you're not using slideTimer within your returned JSX). In this case, you're better off using a ref to store your interval id:
const slideTimerRef = useRef(0); // instead of the `slideTimer` state
...
clearInterval(slideTimerRef.current);
slideTimerRef.current = null;
...
slideTimerRef.current = setInterval(handleNext, slideDuration);

React event-handler shows the same index value

have a simple React component that displayes a character and should call a handler when clicked, and supply a number. The component is called many times, thus displayed as a list. The funny thing is that when the handler is called, the supplied index is always the same, the last value of i+1. As if the reference of i was used, and not the value.
I know there is a javascript map function, but shouldn't this approach work too?
const charComp = (props) => {
return (
<div onClick={props.clicked}>
<p>{props.theChar}</p>
</div>
);
deleteHandler = (index) => {
alert(index);
}
render() {
var charList = []; // will later be included in the output
var txt = "some text";
for (var i=0; i< txt.length; i++)
{
var comp =
<CharComponent
theChar = {txt[i]}
clicked = {() => this.deleteHandler(i)}/>;
charList.push(comp);
}
Because by the time you click on a letter, i is already 9 and it will remain 9 since the information is not held anywhere.
If you want to keep track of the index you should pass it to the child component CharComponent and then pass it back to the father component when clicked.
const CharComponent = (props) => {
const clickHandler = () => {
props.clicked(props.index);
}
return (
<div onClick={clickHandler}>
<p>{props.theChar}</p>
</div>
);
};
var comp = (
<CharComponent theChar={txt[i]} index={i} clicked={(index) => deleteHandler(index)} />
);
A little codesandbox for ya

returning inside for loop breaks the loop

I'm trying to render a certain number of divs based on the pageCount value I have in my app. My approach is as follows
render() {
const { pageCount } = this.props;
function renderPages() {
for (let i = 0; i < pageCount; i++) {
return <div>{i}</div>;
}
}
return <div style={{ textAlign: "center" }}>{renderPages()}</div>;
}
So here pageCount value i get is 5. I want to render 5 divs but when returning, the loop breaks. How can I fix this?
As you noticed, your loop is breaking early due to the return. Instead, you can render an array of JSX. One way could be to create an array in your renderPages and .push() your <div>s into that:
function renderPages() {
const divs = [];
for (let i = 0; i < pageCount; i++){
divs.push(<div>{i}</div>);
}
return divs;
}
return (
<div style={{textAlign:'center'}}>
{renderPages()}
</div>
)
Or, you can populate an array using Array.from() with a mapping function:
function renderPages() {
return Array.from({length: pageCount}, (_, i) => <div>{i}</div>);
}
return (
<div style={{textAlign:'center'}}>
{renderPages()}
</div>
)
Returning inside the for loop always breaks the loop. So instead you can use .map Read more about it here
render(){
const {pageCount} = this.props;
function renderPages(){
return new Array(pageCount).map((item, index) => (
<div>{item}</div>
));
}
return (
<div style={{textAlign:'center'}}>
{renderPages()}
</div>
)
}

How to loop through and display buttons that have an onClick and pass a different variable to another function?

I have the following ReactJS code which I am using to render a number of buttons which will allow the user to navigate around the data returned from an API (a bunch of paginated images).
I have the multiple buttons displaying but they send i as 19 (the end value in the loop) to the handlePageClick() function.
How can I get the value of i to be passed to my handlePageClick() function?
handlePageClick( button ) {
console.log( button );
this.setState( prevState => {
return {
currentPage: button
}
}, () => {
this.loadMedia();
});
}
render() {
// figure out number of pages
const mediaButtonsNeeded = parseInt( this.state.totalMedia / this.state.perPage )
var mediaButtons = [];
for (var i = 0; i < mediaButtonsNeeded; i++) {
mediaButtons.push(<button onClick={() => this.handlePageClick(i)}>{i}</button>);
}
return (
<div>
<div>
<h1>Media test</h1>
{media}
{mediaButtons}
</div>
</div>
)
Since var is global scope variable declaration, your loop always rewrite i to next value.
Easiest solution is just replacing from var to let.
for (let i = 0; i < mediaButtonsNeeded; i++) {// your code...
This should work:
for (var i = 0; i < mediaButtonsNeeded; i++) {
mediaButtons.push(<button onClick={() => this.handlePageClick(i+"")}>{i}</button>);
}

Categories

Resources