React : Pagination is not Working Smoothly - javascript

I am working on pagination in React . I am fetching data from online server through API . API was built in Loopback . I have some problem regarding next and previous button . I make logic for pagination if user want to render next page data it will click on next , If User want to load previous page data it click on previous button . With my logic Next button is working fine , it rendering the next page data if user click on next button but problem is that if user want to click on previous button it loading next page data not like previous page data. Somebody please help me how I can make this type of pagination if user click on next it must load next page data or user want to load previous page data it must load previous page with prev button .
Code
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
Item: 5,
skip: 0
}
this.handleClick = this.handleClick.bind(this);
}
urlParams() {
return `http://localhost:3001/meetups?filter[limit]=${(this.state.Item)}&&filter[skip]=${this.state.skip}`
}
handleClick() {
this.setState({skip: this.state.skip + 1})
}
render() {
return (
<div>
<a href={this.urlParams()}>Example link</a>
<pre>{this.urlParams()}</pre>
<button onClick={this.handleClick}>Change link</button>
</div>
)
}
}
ReactDOM.render(<Example/>, document.querySelector('div#my-example' ))

You have just write btnClick(e) in one way that adding the skip:skip+10 but if you need previous data it should subtract skip:skip-10
So the solution is
btnClick(e,type){
const Item=this.state.Item;
const skip=this.state.skip;
If(type === “next”){
this.setState({
Item,
skip:skip-10
},()=> this.getData())
} else {
this.setState({
Item,
skip:skip+10
})
}
}
// call function this.btnClick(e, “next”)
Or prev for pervious

If you look at the documentation for the Pagination component at https://react.semantic-ui.com/addons/pagination/ , You will see that you are passing this.btnClick into onClick={this.btnClick} when it should be passed into onPageChange={this.btnClick}
<Pagination
boundaryRange={0}
onPageChange={this.btnClick}
defaultActivePage={1}
ellipsisItem={null}
firstItem={null}
lastItem={null}
siblingRange={1}
totalPages={10}
/>
The parameters that are passed into onPageChange are as such:
onPageChange(event: SyntheticEvent, data: object)
It is not clear the difference between Item and Skip in state and what you are trying to accomplish with your btnClick function. It does not look like you are accounting for the possibility that you could click a numbered page on the pagination component instead of just the nextPage and PrevPage` buttons.
I'm going to assume Item indicates the current page you want to be on, in which case inside your btnClick function, you can grab the next active page from the data parameter (this will return the paginated number of the next page - whether you click that number directly or click nextPage).
btnClick(event, data){
const nextPage = data.activePage;
this.setState({item: nextPage});
}

Related

Changing content dynamically inside react-native-pager-view (or any other)

i'm working on a project where i'm going to be displaying details and information about a certain book page by page inside a pager view as page components, the book contains 500+ pages so i can't just create 500 page components like that and insert them into the pager..what i thought is i can get a specific page, render its component, alongside the previous, and the next page only..and when the user swipes to the next/previous page i would change the component state, and have it re-render with the new 3 pages, the current one, the previous, and the next one. the logic in my head makes perfect sense, but it just won't work when i try to apply it.
can anyone help me, guide me to certain videos that explain this principal more? i feel like i'm missing something.
the code goes like this:
first i have the PagesContainer, here i will create the PagesDetails component(s) based on the current page, and having these pages in react-native-pager-view (you can suggest me a better option). for testing purpose only, i set the swipe end callback (onPageSelected) to increment the current page number state, which would then cause the component to re-render and create the new page component(s), that happens only when the user swipes to new page of course:
function PagesContainer({ currentPageNumber, setCurrentPageNumber }) {
const [pageComponents, setPageComponents] = useState([]);
useEffect(() => {
let compArr = [];
compArr.push(<PageDetails key="current" pageNumber={currentPageNumber} />);
if (currentPageNumber > 1) {
compArr.unshift(<PageDetails key="previous" pageNumber={currentPageNumber - 1} />)
}
if (currentPageNumber <= 500) {
compArr.push(<PageDetails key="next" pageNumber={currentPageNumber + 1} />)
}
setPageComponents(compArr);
}, [currentPageNumber])
return (<PagerView style={{ flex: 1 }}
initialPage={currentPageNumber == 1 ? 0 : 1}
layoutDirection={"rtl"}
onPageSelected={(PageSelectedEvent)=>{setCurrentPageNumber(currentPageNumber + 1)}}
>
{pageComponents.map(page => {
return page;
})}
</PagerView>)
}
and then here i have my PageDeatails component where i simply display texts and details of the page, i take the data from the bookData object which is imported at the very top of the code file:
function PageDetails({ pageNumber }) {
const [pageContent, setPageContent] = useState(null);
useEffect(() => {
setPageContent(bookData[pageNumber]["pageContent"]);
}, []);
return (
<View>
{pageContent && <View>
{pageContent.map(item => {
return (<Text>item</Text>)
})}
</View>
}
</View>
)
}
The logic makes perfect sense in my head but it just doesn't work when i test it..what am i missing? what am i doing wrong?
use the PagerView reference using useRef also store the page index and pass to initialPage get current page index from onPageSelected callback
like:
initialPage={currentPageIndex}

ReactJS: How To Get Input Value From The Prompt Box And Update It To Database

I'm building a MERN app, I want to let user edit the food name in the prompt box by clicking on the Edit button.
I was following the instructions in this link: [https://stackoverflow.com/questions/54993218/reactjs-using-alert-to-take-input-from-user]
The issue is when I click on the Edit button and type in the prompt then click OK, it will receive the null value for the first time, but it won't update the database.
And then when I click the Edit button again, without input anything to it then press OK, it will receive the value from the first time input and update it to database (like a delay).
What I want is: when click on the Edit button, it will display the prompt box and take the value from the prompt box and update to the database when the user clicks OK.
Is there any way I can fix this? Thank you everyone!
Here's my demo: gif
Here's my code:
function FoodListTable(props) {
/* Definition of handleClick in component */
const [newFoodName, setNewFoodName] = useState("")
const handleEdit = () => {
const enteredFood = prompt('Please enter your new food:')
setNewFoodName(enteredFood)
console.log(newFoodName)
if (newFoodName) {
Axios.put("https://mern-lefood.herokuapp.com/update", {
newFoodName: newFoodName,
id: props.val._id
})
}
}
return (
<button onClick={handleEdit}>Edit</button>
)
}
React this.setState, and useState does not make changes directly to the state object.
React this.setState, and React.useState create queues for React core to update the state object of a React component.
So the process to update React state is asynchronous for performance reasons. That’s why changes don’t feel immediate.
Try below code that's works !
function FoodListTable(props) {
/* Definition of handleClick in component */
const [newFoodName, setNewFoodName] = useState("");
const handleEdit = () => {
const enteredFood = prompt('Please enter your new food:');
setNewFoodName(enteredFood);
if (enteredFood) {
Axios.put("https://mern-lefood.herokuapp.com/update", {
newFoodName: enteredFood,
id: props.val._id
})
}
}
return (
<button onClick={handleEdit}>Edit</button>
)
}
For more detail Click here

How to add a class of "active" to individual elements in a functional navigation component in react?

This is something quite simple but somehow resulted in a crazy rabbit hole.
This link shows what I want:
https://www.w3schools.com/howto/howto_js_active_element.asp
Nothing special, now the thing becomes hairy for me when the elements in the navbar are rendered from an array of objects (from the specs). The approach I am following is basically rendering a list of buttons, this list of buttons is the state, since supposedly when you update a state it triggers a re-render, then when a button is clicked it "sets" the active class to false on the entire array-state then activates it only for the clicked one. So far it works.
The problem is that the active class is rendered two steps behind. One for the moment when the class in the array-state's elements are set to false, the other when the clicked element gets updated.
As far as I understand useState and setState are queues, hence those are applied asynchronously on each render, in order to avoid that and get the renders to show the current state, useEffect is utilized.
Now the thing is that I am not sure how to apply useEffect in order to achieve the immediate render of the "active" class.
This is the code I have:
import { options } from 'somewhere...'
export default function SideMenu(props){
let auxArr = []
let targetName
const [stateOptions, setStateOptions] = useState([...options])
const [currentOption, SetCurrentOption] = useState({})
function activeOption(e){
// this helps with event bubbling
if (e.target.tagName == "P" || e.target.tagName == "SPAN"){
targetName = e.target.parentElement.id
} else if (e.target.tagName == "IMG"){
targetName = e.target.parentElement.parentElement.id
} else {
targetName = e.target.id
}
// since the main state is an array of objects I am updating it
// in three steps, first the current object is "activated"
// then the main array-state gets "inactivated" to erase all
// the previous "active" classes, finally the activated object
// replaces the corresponding inactive object in the main state.
let targetElement = stateOptions.filter(e => e.id==targetName)[0]
SetCurrentOption({
id: targetElement.id,
activity:true,
img: targetElement.img,
name: targetElement.name
})
// first the "classes" are set to false, then the
// "activated" object replaces the corresponding one
// in the main object, from here comes the two
// steps delay.
auxArr = [...stateOptions]
auxArr.forEach(e => e.activity=false)
setStateOptions(auxArr)
const newOptions = stateOptions.map(e =>
e.id==currentOption.id ? currentOption : e
)
setStateOptions(newOptions)
}
return(
<aside className={styles.sideDiv}>
<nav>
{stateOptions.map(({id, img, name, activity, link}) => {
return(
<button key={id} id={id} onClick={activeOption} className={activity?styles.active:""}>
<Image src={img}/>
<p className={timeColor.theme}> {name} </p>
</button>
)
})}
</nav>
</aside>
)
}
Thanks in advance for any help you can provide.

Targeting only the clicked item on a mapped component ( React quiz trivia App)

i'm trying to develop an App with React using the Open trivia Api. I have mapped a button component (using material ui) to show the different answers for each question. I'm struggling now to target only the clicked one to apply a css property: if the answer is correct should become green, else red. The problem is the fact that once i click, all button become red or green. I tried to store the index in a state and compare the real index, but it doesn't work. here is my code:
in the main APP.js
const [clickedOne, setClickedOne] = useState({
clickedIndex: null,
});
useEffect(() => {
grabData();
}, []);
const handleClick = (choice, ke) => {
setChoice(choice);
if (choice === data.correct_answer) {
setIsCorrect(true);
} else {
setIsCorrect(false);
}
setClickedOne({ clickedIndex: ke });
grabData();
};
The mapped button inside the Render:
{answers.map((answer, index) => {
return (
<ContainedButtons
choice={handleClick}
answer={answer}
correct={data.correct_answer}
isCorrect={isCorrect}
key={index}
id={index}
clicked={clickedOne}
/>
);
})}
Inside the Button component:
const backStyle = () => {
if (clicked === id) {
if (isCorrect) {
return "green";
} else if (isCorrect === false) {
return "red";
} else {
return null;
}
}
};
return (
<div className={classes.root}>
<Button
style={{ backgroundColor: backStyle() }}
value={answer}
onClick={() => choice(answer, id)}
variant="contained"
>
{decodeURIComponent(answer)}
</Button>
When i check now inside the backstyle function if the clicked===id, now nothing happens anymore. Without that if check, i would have all buttons red or green.
Thank you guys for the help!
I have looked at your codesandbox demo, there are alot of other problems apart from the one your question is about.
First of all, each time you make a request to the API to fetch next question, you are making a request to get 10 questions instead of 1. API request URL contains a query parameter named amount which determines how many questions will be fetched on each request. Change its value to 1.
"https://opentdb.com/api.php?amount=1&encode=url3986"
Secondly, there is a lot of unnecessary code and unnecessary use of useState hook. You only need 2 things to be stored in the state, data and answers
const [data, setData] = useState({});
const [answers, setAnswers] = useState([]);
Now, coming to the original problem of detecting which button is clicked and correctly updating its background color.
To achieve the desired functionality, take following steps:
create couple of CSS classes as shown below
button.bgGreen {
background-color: green !important;
}
button.bgRed {
background-color: red !important;
}
pass a handleClick function from App component to ContainedButtons component. When a button is clicked, this click handler will be invoked. Inside the handleClick function, get the text and the button that was clicked using Event.target and depending on whether user answered correctly or not, add appropriate CSS class, created in step 1, on the button that was clicked.
Instead of using index as key for ContainedButtons in map function, use something that will be unique each time. This is needed because we want React to not re-use the ContainedButtons because if React re-uses the ContainedButtons component, then CSS classes added in step 2 will not be removed from the button.
Here's a working codesanbox demo of your app with the above mentioned steps.
In this demo, i have removed the unnecessary code and also changed the key of ContainedButtons inside map function to key={answer.length * Math.random() * 100}. You can change it to anything that will ensure that this key will be unique each time.

Controlling a checkbox in react

I'm trying to create filters in react, where i manipulate the url to return me products based on colours, cost etc
its working so if you change ?color=red to ?color=white in the url it will display different products on the page
it's also working whereby if you select the colours in my checkbox filter component it will update the url and then display the new products. i.e click on red will change the url from /sport to /sport?color=red and then returns me just the products with red
however this is my problem
if I manually change the url, I then want the checkbox checked so I tried to do this:
checked={option.text === this.getParams() ? true : false}
this does actually work but then I lose the ability to actually select and deselect the checkbox. any ideas how I can get it to do both? I guess making it a controlled and uncontrolled component simultaneously??
You need to store the filters in the state. like in your constructor you can init your state with the query parameter and then change the state upon checkbox change.
You could try something like this. You will need to change this code according to your usage, here I am assuming, this.getParams('color') will return an array of all the selected colors.
constructor state init
constructor(props) {
super(props);
this.state = {
filters: this.getParams('color') // consedering, it will return array
}
}
default check the checkbox
defaultChecked ={this.state.filters.indexOf(option.text) === -1 ? false : true}
onChange={() => this.toggleCheckbox(option.text)}
for toggling it
// if not present, then add it
// else remove it
toggleCheckbox(option) {
if (this.state.filters.indexOf(option) === -1) {
this.setState({
filters: [...this.state.filters, option]
})
} else {
this.setState({
filters: this.state.filters.filter(text => text !== option)
})
}
}
You should set the state of the checkbox in the component state, and then update that state when it's clicked. You can set the initial state based on the url on construct or mount.
Something like this:
constructor(props) {
super(props);
const isChecked = this.props.match.params.checkbox === 'true';
this.state = {
checkbox: isChecked
}
}
And then in your checkbox:
<input type="checkbox" checked={this.state.checkbox} onChange={() => this._toggleCheckbox()} />
And the method to turn it on and off would be something like:
toggleCheckbox() {
this.setState({
checkbox: !this.state.checkbox // will toggle to the opposite of the current state
});
}
Note that this is has not been tested but has been written based on the information you gave. The principle behind this is what you need to do. It may also be useful to set the state of the checkbox initially within componentDidMount(), rather than constructor(), but that's up to you. The onChange function of the checkbox uses ES6, but you could bind the function if you prefer or do not use ES6 with this._toggleCheckbox().bind(this)
Edit
To update the checkbox when the url is changed, rather than updating it on click, you could change the toggle method to redirect the browser, and then update the checkbox within componentWillReceiveProps.
Taken from my own code with react-router you can use 'this.props.match.params' to find the url parameters. I use react-router-dom package to update the url. So for instance:
This will give you access to this.props.history.
import { withRouter } from 'react-router-dom';
toggleCheckbox() {
// Check the current state of the checkbox and update the url to the opposite
let toCheck = this.props.match.params.checkbox === 'true' ? 'false' : 'checked';
this.props.history.push('/?checkbox=' + toCheck);
}
componentWillReceiveProps(newProps) {
// Check the new url and update the checkbox if it is different from the checkbox state
if(newProps.match.params.checkbox != this.state.checkbox) {
this.setState({checkbox: newProps.match.params.checkbox});
}
}

Categories

Resources