I am using pagination component from following link, in that I am unable to update pageLimit value based on user selection how many rows will display.
https://scotch.io/tutorials/build-custom-pagination-with-react
https://codesandbox.io/s/competent-river-xneqb
Initially the page limit will be 10. Once the user clicks the button the page limit will be changed to 15. But the paginations page limit is not changing even after updating the component also.
" import Pagination from "./components/Pagination";
class App extends Component {
state = {
pageLimit : 10
};
handlePageLimit = () => {
this.setState({pageLimit : 15})
console.log("pagelimit inside fun",this.state.pageLimit)
}
return (
<div className="d-flex flex-row py-4 align-items-center">
<Pagination
totalRecords={this.state.totalCountries}
pageLimit={this.state.pageLimit}
pageNeighbours={1}
onPageChanged={this.onPageChanged}
/>
<button onClick={this.handlePageLimit}>Click</button>
</div>
);"
Please help me to resolve such that when the button clicks the page limit should be changed to 15.
This guide has so many issues it would be better to just find another ready solution and implement it from scratch. If you want to keep using that you should start here:
Building on Tom Finney's comment, you would need to use componentDidUpdate and then work from there manually updating stuff in your render function:
componentDidUpdate(prevProps) {
if (prevProps.pageLimit !== this.props.pageLimit) {
this.pageLimit = this.props.pageLimit;
this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
this.gotoPage(1); // If you want to reset to first page on limit change
}
}
Totally not worth the effort.
Related
Intro / Context
I am trying to build an application using React that allows for image or video display based on a chosen menu item. I am currently using Advanced Custom Fields within WordPress to build my data objects and using graphQL to query them into my project.
I want the application to display either a video component or an image component. This choice will be determined through conditional rendering and based on the contents of the object's fields.
Each object contains a title, an image field and a video field. If the entry in question should be displayed as an image the video field will be set as the string 'null'. All fields will return strings regardless.
I am using useState to target a particularly active field, however, despite triggering a change in state conditional rendering does not appear to change.
The Application
This is my approach
function Display({ objects }) {
const [setVisualOption, changeVisualOption] = useState(false);
const [appState, setState] = useState({
myObjects: objects,
activeTitle: "null",
activeImage: "null",
activeMediaUrl: "null",
});
function toggleActive(index, trackIndex) {
setState({
...appState,
activeTitle: appState.myObjects[index].title,
activeImage: appState.myObjects[index].image[0].mediaItemUrl,
activeMediaUrl: appState.myObjects[index].mediastreamurl,
});
changeVisualOption(appState.activeImage.includes("null"));
}
useEffect(() => {}, [
appState.activeTitle,
appState.activeImage,
appState.activeMediaUrl,
setVisualOption,
]);
return (
<div className="display">
<div className="list-box-right>
{appState.myObjects.map((element, index) => (
<>
<div
key={index}
className="menu-item"
onClick={() => {
toggleActive(index);
}}
>
{element.title}
</div>
</>
))}
</div>
<div className="right-grid">
{setVisualOption ? (
<VideoComponent activeImage={appState.activeImage}></VideoComponent>
) : (
<ImageComponent activeImage={appState.activeImage}></SingleImage>
)}
</div>
</div>
);
}
The summarise, to component takes objects as prop which are being passed down from another component making the graphQL query. I am then setting the initial values of useState as an object and setting an activeTitle, activeImage and activeMediaUrl as null.
I am then using a function to toggle the active items using the setState modifier based upon the index that is clicked within the return statement. From there I am using setVisualOption and evaluating whether the activeImage is contains 'null' (null.jpg), if this is true setVisualOption will be set to true allowing the Video Component to be rendered
The Problem
To be clear, there are no errors being produced and the problem is a slight rendering issue where it requires double clicks to alter the state and trigger the correct response from the tertiary operator.
The issue is within the conditional rendering. If I set my object fields to all images and return only the Image Component there are no issues, and the state change can be seen to register visually as you click down the listed options.
It is only when I introduce the conditional rendering that there is a delay, the first click does not generate the correct response from the component I am trying to display however the second click triggers the right response.
As you can see, I am also using useEffect to try and trigger a rendered response when any of the described states change but still encounter the same problem.
Does anyone know what is the cause of this bug? when looking at the console.log of setVisualOption is not appearing as true on first click when it aught to.
Any insights would be great thanks
You set your visual option right after you set your appState, this is why appState.activeImage in changeVisualOption is not updated because state updates in React is asynchronous. You can either use useEffect to update visual option when the appState changes or you can use appState.myObjects[index].image[0].mediaItemUrl in changeVisualOption
function toggleActive(index, trackIndex) {
setState({
...appState,
activeTitle: appState.myObjects[index].title,
activeImage: appState.myObjects[index].image[0].mediaItemUrl,
activeMediaUrl: appState.myObjects[index].mediastreamurl,
})
changeVisualOption(appState.myObjects[index].image[0].mediaItemUrl.includes("null"))
}
or
useEffect(() => {
changeVisualOption(appState.activeImage.includes("null"))
}, [appState])
In my Gatsby app I have a page that includes a pagination component.
By default, the path is: '/?page=1' and clicking the pagination buttons changes to '/?page=2', '/?page=3' ...
I am "saving" the page number in the path because I want the browser to remember the page number the user was on previously, in case he clicks the go back button of the browser.
The problem with this approach is that every time the path is changed, the page automatically is scrolling to the top, and I do not want it to scroll to the top.
Does anyone know the way to prevent that?
This is the default Gatsby behavior (known as Scroll Restoration). You have exposed the useScrollRestoration hook in order to play with this behavior (and change it accordingly to your specifications). For example:
import { useScrollRestoration } from "gatsby"
import countryList from "../utils/country-list"
export default function PageComponent() {
const ulScrollRestoration = useScrollRestoration(`page-component-ul-list`)
return (
<ul style={{ height: 200, overflow: `auto` }} {...ulScrollRestoration}>
{countryList.map(country => (
<li>{country}</li>
))}
</ul>
)
}
In your case, you should add your element class name.
Related GitHub threads:
https://github.com/gatsbyjs/gatsby/issues/27349
https://github.com/gatsbyjs/gatsby/issues/19480
I have looked around for an answer to this - the closest I found being this question - but there is I think a significant difference in my case (the fact that it starts to get into the parent holding the state of its children's... children) which has finally lead to me asking for some clarification.
A very simple example of what I mean is below (and will hopefully better illustrate what I'm asking):
Suppose we have a bunch of book documents like
bookList = [
{
title: "book 1",
author: "bob",
isbn: 1,
chapters: [
{ chapterNum: 1, chapterTitle: "intro", chapterDesc: "very first chapter", startPg: 2, endPg: 23 },
{ chapterNum: 2, chapterTitle: "getting started", chapterDesc: "the basics", startPg: 24, endPg: 45 }
]},
{
title: "book 2" ... }
]
So main point being these embedded objects within documents that could be very long and as such may be collapsed / expanded.
And then here is a rough sample of code showing the components
class App extends Component {
constructor(props) {
super(props);
this.state = {
books: bookList,
focusBook: null
}
this.updateDetailDiv = this.updateDetailDiv.bind(this);
}
updateDetailDiv(book) {
this.setState(
{ focusBook: book}
);
}
render() {
return(
<BookList
bookList = {this.state.books}
updateDetailDiv = { this.updateDetailDiv }
/>
<BookDetail
focusBook = { this.state.focusBook }
/>
);
}
}
const BookList = props => {
return (
props.bookList.map(item=>
<li onClick={()=> props.updateDetailDiv(item)}> {item.title} </li>
)
);
}
const BookDetail = props => {
return (
<div className="bookDetails">
{ props.focusBook != null
? <div>
{props.focusBook.title},
{props.focusBook.author},
{props.focusBook.isbn}
Chapters:
<div className="chapterList">
{ props.focusBook.chapters.map(item=>
<span onClick={()=>someFunction(item)}>{item.chapterNum} - {item.chapterName}</span>
)}
</div>
<div id="chapterDetails">
This text will be replaced with the last clicked chapter's expanded details
</div>
</div>
: <div>
Select A Book
</div>
})
}
someFunction(item) {
document.getElementById('chapterDetails').innerHTML = `<p>${item.chapterDesc}</p><p>${item.startPg}</p><p>${item.endPg}</p>`;
}
So my problem is that i'm not sure what the best approach is for handling simple cosmetic / visual changes to data in functional stateless components without passing it up to the parent component - which is fine and makes sense for the first child - but what happens when many children will have their own children (who may have their own children) --> all requiring their own rendering options?
For example - here the App component will re-render the DetailDiv component (since the state has changed) - but I don't want the App also handling the DetailDiv's detailed div. In my example here its all very simple but the actual application I'm working on has 2 or 3 layers of embedded items that - once rendered by App - could realisticially just be modified visually by normal JS.
SO in my example you'll see I have a someFunction() in each Chapter listing - I can make this work by writing a separate simple 'traditional JS DOM function' (ie: target.getElementById or closest() -- but i don't think i'm supposed to be using normal JS to manipulate the DOM while using React.
So again to summarize - what is the best way to handle simple DOM manipulation to the rendered output of stateless components? Making these into their own class seems like overkill - and having the 'parent' App handle its 'grandchildren' and 'great-grandchildren's state is going to be unwieldy as the Application grows. I must be missing an obvious example out there because I haven't seen much in the way of handling this without layers of stateful components.
EDIT for clarity:
BookDetail is a stateless component.
It is handed an object as a prop by a parent stateful component (App)
When App's state is changed, it will render again, reflecting the changes.
Assume BookDetail is responsible for displaying a lot of data.
I want it so each of the span in BookDetail, when clicked, will display its relevant item in the chapterDetail div.
If another span is clicked, then the chapterDetail div would fill with that item's details. (this is just a simple example - it can be any other pure appearance change to some stateless component - where it seems like overkill for a parent to have to keep track of it)
I don't know how to change the UI/appearance of the stateless component after it is rendered without giving it state OR making the parent keep track of what is essentially a 'substate' (since the only way to update the appearance of a component is to change its state, triggering a render).
Is there a way to do this without making BookDetail a stateful component?
You can add a little bit of simple state to functional components to track the selected index. In this case I would store a "selected chapter index" in state and then render in the div the "chapters[index].details", all without manipulating the DOM which is a React anti-pattern.
The use-case here is that the selected chapter is an internal detail that only BookDetail cares about, so don't lift this "state" to a parent component and since it is also only relevant during the lifetime of BookDetail it is rather unnecessary to store this selected index in an app-wide state management system, like redux.
const BookDetail = ({ focusBook }) => {
// use a state hook to store a selected chapter index
const [selectedChapter, setSelectedChapter] = useState();
useEffect(() => setSelectedChapter(-1), [focusBook]);
if (!focusBook) {
return <div>Select A Book</div>;
}
const { author, chapters, isbn, title } = focusBook;
return (
<div className="bookDetails">
<div>
<div>Title: {title},</div>
<div>Author: {author},</div>
<div>ISBN: {isbn}</div>
Chapters:
<div className="chapterList">
{chapters.map(({chapterName, chapterNum}, index) => (
<button
key={chapterName}
onClick={() => setSelectedChapter(selectedChapter >= 0 ? -1 : index)} // set the selected index
>
{chapterNum} - {chapterName}
</button>
))}
</div>
// if a valid index is selected then render details div with
// chapter details by index
{chapters[selectedChapter] && (
<div id="chapterDetails">
{chapters[selectedChapter].details}
</div>
)}
</div>
</div>
);
};
DEMO
There is some approaches you can do to solve this problem.
First, you don't need to create some class components for your functional components, instead, you can use react hooks, like useState so the component can control it's own content.
Now, if you don't want to use React Hooks, you can use React Redux store to manage all your states: you can only change the state values using the Redux actions.
Happy coding! :D
I am using Facebook's like button as generated by facebook's like button configurator. However in order to get facebook-sdk to finish loading before the Like button, I had to use something called react-load-script and make a my own wrapper component for the like button html I got from the configurator.
my like button
class Like extends React.Component {
state = {
facebookLoaded: false
};
handleFacebookLoaded = () => this.setState({
facebookLoaded: true
});
FacebookSDK = () => <>
<div id="fb-root"></div>
<Script
async defer crossOrigin="anonymous"
url="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v3.3&appId=391623981325884&autoLogAppEvents=1"
onLoad={this.handleFacebookLoaded}
/>
</>;
render() {
return <>
<this.FacebookSDK />
{this.state.facebookLoaded
? <div class="fb-like" data-href={this.props.url} data-width="" data-layout="button_count" data-action="like" data-size="large" data-show-faces="true" data-share="true" />
: null}
</>;
}
}
In my code all the script loading stuff actually happens in App.jsx, but I moved it into one class just to show a simple version.
This part seems to work fine, the issue lies when changing the url passed to data-href.
I checked the react dom in the browser and the data-href is actually being updated properly, however this does not affect the actual url that is being used by the like button, unless I do a full page refresh. I'm assuming this has to do with how the data-href is being used by facebook-sdk. (edit: after testing I'm not sure anymore)
I've found many questions about this on Stack Overflow, however none of them seem to be based off the CDN version of facebook buttons
From what I understand, the div containing the href needs to be placed out and back into the DOM in order for the facebook-sdk to detect a change, but I don't know how to do this in react without a full page refresh. Also I'm not certain this is even the right solution.
-- Update --
I just noticed something else that seems like useful information. If I navigate to the page with the like button, then it doesn't show up. It will only show up if the page refreshes. I tested it by moving the part that loads the script into the like component (like in the example shown above) and that didn't change the behavior at all.
-- more experimenting --
I wrote an event handler that takes all the facebook related jsx out of the dom and back in (by toggling a button) However when all the code goes back into the dom (both jsx and html), the UI for the button does not come back. I'm really now sure how this is possible as I'm literally reloading the script and everything facebook related so this should be equivalent to a page refresh no?
I fixed the issue thanks to misorude. The part I was missing was calling window.FB.XFBML.parse(). I didn't realize I could access FB the same way using the CDN. If anyone is looking for a react solution here is the working code:
class Like extends React.Component {
constructor(props) {
super(props);
this.state = {
url: props.url,
}
}
handleChangePage() {
let likeBtn = document.createElement('div');
likeBtn.className = "fb-like";
likeBtn.setAttribute("data-href", this.props.url);
likeBtn.setAttribute("data-width", "");
likeBtn.setAttribute("data-layout", "button_count");
likeBtn.setAttribute("data-action", "like");
likeBtn.setAttribute("data-size", "large");
likeBtn.setAttribute("data-show-faces", "true");
likeBtn.setAttribute("data-share", "true");
let likePanel = document.getElementById("like-panel");
likePanel.removeChild(likePanel.childNodes[0]);
likePanel.appendChild(likeBtn);
window.FB.XFBML.parse(likePanel)
this.setState({ url: this.props.url });
}
componentDidMount() {
this.handleChangePage();
}
render() {
if(this.props.url !== this.state.url)
this.handleChangePage();
return <div id="like-panel">
{this.props.facebookLoaded
? <div className="fb-like" data-href={this.props.url} data-width="" data-layout="button_count" data-action="like" data-size="large" data-show-faces="true" data-share="true" />
: null}
</div>;
}
}
I moved the CDN out of this component so that it only loads the sdk once for the whole app.
I am using the react instant search by algolia and i have a requirement to show an overlay every time there is result.
so i want to handle it via onSearchStateChange function provided by algolia. but i am still puzzled where to get the total hits. i already have an idea which is very quick like using the results displayed in the by extracting the numbers via jquery. but i don't want to do it. is there other way you can suggest?
onSearchStateChange(nextState) {
//must get the number of total hits.
nextState = cleanDeep(nextState);
let filters = transformer(nextState);
this.setState({
searchState: nextState,
filters: filters,
searchChanged: true
})
this.sendToAti(filters);
this.addOverlay(); // <--- function that will show the overlay.
location.hash = qs.stringify(nextState);
}
The onSearchStateChange function doesn't contain the searchResults object where you can find the number of hits.
However, we provide a <Stats> widget and a connectStats connector that contain this information. Maybe you could use that?
Basically, as pointed by #Marie and documented in the links she pointed, you need to follow a 3 step process:
1.- Create a custom component:
export default function MyStatefullComponent({ searchResults }){
const hasResults = searchResults && searchResults.nbHits !== 0;
const nbHits = searchResults && searchResults.nbHits;
//Handle State Mutations Here
return return (hasResults) ? <div className="shadow-xl mb-4 ml-4 p-8 ">
Has Results
</div>
:
<div>Has No Results</div>
}
2.- connect to the component using the connector
Once you have your custom component, use the connectStateResults
import { connectStateResults } from 'react-instantsearch-dom';
export default function OtherComponent(){
const StatefullComponent = connectStateComponent(MyStatefullComponent);
return <StatefullComponent />
}
3.- import/add the component to your other component.