display Number of page in React Js using material-ui Pagination - javascript

look at the code below in my App.js Component:
const App = () => {
const [workstations, setWorkstations] = useState([]);
let [page, setPage] = useState(1);
const PER_PAGE = 1;
useEffect(() => {
loadWorkstations();
}, []);
const loadWorkstations = async () => {
const request = await getWorkstations();
const result = request.data;
setWorkstations(result);
};
const count = Math.ceil(workstations.length / PER_PAGE);
const _DATA = usePagination(workstations, PER_PAGE);
const handleChange = (e, p) => {
setPage(p);
_DATA.jump(p);
};
return (
<Pagination
count={count}
size="large"
page={page}
variant="outlined"
color="primary"
onChange={handleChange}
/>
);
};
now the result that i got form this implementation is like this :
but what im looking for should be look like this :
i mean i don't want to see all my pages on the UI and show the rest with "..."
Note: im using #material-ui/lab

Your code seems alright, however, you need to import Pagination from #mui to achieve the desired behavior
import Pagination from '#mui/material/Pagination';

const App = () => {
const [workstations, setWorkstations] = useState([]);
const [page, setPage] = useState(1);
const [postsPerPage, setPostsPerPage] = useState(10);
const [totalPosts, setTotalPosts] = useState();
const lastPostIndex = page * postsPerPage;
const firstPostIndex = lastPostIndex - postsPerPage;
const currentPosts = workstations.slice(firstPostIndex,lastPostIndex);
useEffect(() => {
loadWorkstations();
}, []);
const loadWorkstations = async () => {
const request = await getWorkstations();
const result = request.data;
setWorkstations(result);
setPage(result.length);
};
return (
<Pagination
count={count}
size="large"
page={page}
variant="outlined"
color="primary"
numberOfPages={totalPages}
totalPosts={currentPosts.length}
postsPerPage={postsPerPage}
setPage={page}
/>
);
};

Related

React: How to correctly call a function to load images

As the title states, I'm attempting to load images as soon as my react app loads.
I have a .jsx file, where there is a function called getHomePage().
The getHomePage() contains 4 functions.
When the getHomePage() function is called, it renders a dropdown. The dropdown contains an onChange event, where a user is able to select a collection of images. When this collection is selected, it calls a function called collectionChanged(e.target.value).
Goals:
What I want is for the images to load as soon as the application starts. Essentially, I want to call the collectionChanged(e.target.value) function when the application loads. So, I no longer want a user to select a collection, but for the collection of images to load as soon as the app loads.
How do I go about doing this? I hope all the above explanation made sense.
File: home.jsx
function getHomePage() {
const [token, setToken] = useState("");
const [visibility, setVisibility] = useState(false);
const [NFTBalances, setNFTBalances] = useState();
const [collection, setCollection] = useState();
const [nft, setNft] = useState();
const { Moralis } = useMoralis();
const handleChangeCollection = async (col) => {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
query.ascending("rank");
const topNFTs = query.limit(8);
const results = await topNFTs.find();
setNFTBalances(results);
};
}
const handleSelectToken = async (num, col) => {
if (num && col) {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
console.log(num);
query.equalTo("tokenId", num);
let selectedNFT = await query.first();
selectedNFT = selectedNFT.attributes;
console.log(selectedNFT);
setNft(selectedNFT);
setVisibility(true);
}
};
// FUNCTION I WANT TO CALL ONLOAD
const collectionChanged = async (col) => {
setCollection(col);
handleSelectToken(token, col);
handleChangeCollection(col);
};
const addToNFTs = async (col) => {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
query.ascending("rank");
query.limit(4);
const topNFTs = query.skip(NFTBalances.length);
const results = await topNFTs.find();
setNFTBalances(NFTBalances.concat(results));
}
return (
<>
// DROP DOWN SECTION
<div>
<select onChange={(e) => collectionChanged(e.target.value) }>
<option value="">Select a Collection</option>
<option value={"myCollection"}>My Collection</option>
</select>
</div>
<div className="row">
{NFTBalances && NFTBalances.map((nft, index) => {
return (
<div className="col-xxl-3 col-xl-3 col-lg-6 col-md-6">
<div className="card items">
<Card key={index} onClick={() =>
handleSelectToken(nft.attributes.tokenId,collection)}
cover={ <Image src={nft.attributes.image} /> }>
</Card>
</div>
</div>
);
})}
</div>
</>
);
}
export default getHomePage;
You should use the hook useEffect in order to load your images:
function getHomePage() {
const [token, setToken] = useState("");
const [visibility, setVisibility] = useState(false);
const [NFTBalances, setNFTBalances] = useState();
const [collection, setCollection] = useState();
const [nft, setNft] = useState();
const { Moralis } = useMoralis();
useEffect(() => {
//call your function to load your images
collectionChanged('myCollection')
}, [])
const handleChangeCollection = async (col) => {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
query.ascending("rank");
const topNFTs = query.limit(8);
const results = await topNFTs.find();
setNFTBalances(results);
};
}
const handleSelectToken = async (num, col) => {
if (num && col) {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
console.log(num);
query.equalTo("tokenId", num);
let selectedNFT = await query.first();
selectedNFT = selectedNFT.attributes;
console.log(selectedNFT);
setNft(selectedNFT);
setVisibility(true);
}
};
// FUNCTION I WANT TO CALL ONLOAD
const collectionChanged = async (col) => {
setCollection(col);
handleSelectToken(token, col);
handleChangeCollection(col);
};
const addToNFTs = async (col) => {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
query.ascending("rank");
query.limit(4);
const topNFTs = query.skip(NFTBalances.length);
const results = await topNFTs.find();
setNFTBalances(NFTBalances.concat(results));
}
return (
<>
// DROP DOWN SECTION
<div>
<select onChange={(e) => collectionChanged(e.target.value) }>
<option value="">Select a Collection</option>
<option value={"myCollection"}>My Collection</option>
</select>
</div>
<div className="row">
{NFTBalances && NFTBalances.map((nft, index) => {
return (
<div className="col-xxl-3 col-xl-3 col-lg-6 col-md-6">
<div className="card items">
<Card key={index} onClick={() =>
handleSelectToken(nft.attributes.tokenId,collection)}
cover={ <Image src={nft.attributes.image} /> }>
</Card>
</div>
</div>
);
})}
</div>
</>
);
}
export default getHomePage;

React : how to pass and array from inside a Function to the return (JSX)

I am new to React (and still new to JS too), and i am trying to build my first React project. I am fetching an API , rendering some items, and building a Search Bar that filters out the items rendered.
My filtering function is more or less working, and inside of it, i store the filtered results in let result , but How i should access those results from the return part (JSX area, i think) to loop over them?
This is my code :
import React, { useState, useEffect } from "react";
import ListItem from "./ListItem";
const List = () => {
const [data, setData] = useState();
const [input, setInput] = useState("");
const onInputChange = (event) => {
setInput(event.target.value);
const value = event.target.value.toLowerCase();
let result = [];
result = data.filter((item) =>
item.name.toLowerCase().includes(value.toLowerCase())
);
setInput(result);
};
useEffect(() => {
const getData = async () => {
const response = await fetch(
"https://rickandmortyapi.com/api/character/"
);
const obj = await response.json();
setData(obj.results);
};
getData();
}, []);
return (
<div>
<input type="text" name={input} onChange={onInputChange}></input>
{data &&
data.map((item) => {
return <ListItem key={item.id} character={item} />;
})}
</div>
);
};
export default List;
So far, I can only loop over input which contains the results, like this input && input.map((item) , but that gives me an empty array when the page is loaded , until i make a search.
You just initialise input as a string so just keep input for keeping input value not result data. You can create another state for keeping result OR put result data back on Data variable.
Here I am showing you to keep result data separate.
import React, { useState, useEffect } from "react";
import ListItem from "./ListItem";
const List = () => {
const [data, setData] = useState();
const [searchResult, setSearchResult] = useState();
const [input, setInput] = useState("");
const onInputChange = (event) => {
setInput(event.target.value);
const value = event.target.value.toLowerCase();
let result = [];
result = data.filter((item) =>
item.name.toLowerCase().includes(value.toLowerCase())
);
setSearchResult(result);
};
useEffect(() => {
const getData = async () => {
const response = await fetch(
"https://rickandmortyapi.com/api/character/"
);
const obj = await response.json();
setData(obj.results);
};
getData();
}, []);
return (
<div>
<input type="text" name={input} onChange={onInputChange}></input>
{input===""? data &&
data.map((item) => {
return <ListItem key={item.id} character={item} />;
}):
searchResult &&
searchResult.map((item) => {
return <ListItem key={item.id} character={item} />;
})
}
</div>
);
};
export default List;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
This is separating your original data and search result different.
You need to use a variable to store data after filter:
const [data, setData] = useState([]);
const onInputChange = (event) => {
setInput(event.target.value);
};
const result = data.filter((item) =>
item.name.toLowerCase().includes(input.toLowerCase())
);
return (
...
{result?.map((item) => {
<ListItem key={item.id} character={item} />;
})}
...
)
One possible solution would be to filter while rendering,
In this scenario you would only need to save the the input value (onInputChange):
const onInputChange = (event) => {
setInput(event.target.value);
};
Then while rendering you would need to add the filtering logic:
{ // if input is not empty
data
.filter(item => item.name.includes(input.toLowerCase()))
.map((item) => {
return <ListItem key={item.id} character={item} />;
})

How to display posts on the current page from the API

I'm getting data from Django Rest API and React for Frontend, and I need to create the pagination with posts. I did it all in pagination component. I created the state with current page and I'm changing it by clicking on the page button in component like this:
const Paginator = () => {
const [count, setCount] = useState(null);
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(null);
const [nextPage, setNextPage] = useState(null);
const [previousPage, setPreviousPage] = useState(null);
const [valid, setValid] = useState(false);
useEffect(() => {
fetch(`http://127.0.0.1:8000/api/software/?p=${currentPage}`)
.then(response => response.json())
.then(data => {
setCount(data.count);
setTotalPages(data.total_pages)
setNextPage(data.links.next);
setPreviousPage(data.links.previous);
setValid(true);
})
}, [currentPage]);
...
return (
<>
{
...
<PbStart style={style} totalPages={range(1, totalPages+1)} setCurrentPage={setCurrentPage} />
...
}
</>
);
};
const PagItem = ({key, handleClick, className, title, name }) => {
return (
<li key={key} onClick={handleClick}>
<Link to='/' className={className} title={`Go to page ${title}`}>
{name}
</Link>
</li>
);
};
const PbStart = ({ style, totalPages, setCurrentPage }) => {
return (
...
{totalPages.map(p => (
<PagItem key={p} handleClick={() => setCurrentPage(p)} title={p} name={p} />
))}
...
);
};
And in posts component I don't know how to change current page, or getting it from the paginaton component. I've written that like this:
const Softwares = () => {
const [softwares, setSoftwares] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [valid, setValid] = useState(false);
useEffect(() => {
fetch(`http://127.0.0.1:8000/api/software/?p=${currentPage}`)
.then(response => response.json())
.then(data => {
setSoftwares(data.results);
setValid(true);
})
}, [currentPage]);
return (
<>
{
...
{softwares.map(s => (
<Article key={s.id} pathname={s.id} title={s.title} image={s.image} pubdate={s.pub_date} icon={s.category.parent.img} categoryID={s.category.id} categoryName={s.category.name} dCount={s.counter} content={s.content} />
))}
...
}
</>
);
};
So, how to do that(get the current page from pagination component or another way)?
I think a Paginator's job is only moving between pages and updating current page state. It should not be fetching data by itself, you can provide functionality to do extra work with props.
I haven't tested this, but this might be a good starting point.
With the example below you'll have a list of articles and then below it next and previous buttons.
In Softwares, as you can see I am passing the same function for handling next and previous pages, you can refactor it to have one function like onPageMove and call this function handleNext and handlePrev.
I added two separate functions if you have want to handle something different in either.
const Paginator = ({
total, // Required: Total records
startPage = 1, // Start from page / initialize current page to
limit = 30, // items per page
onMoveNext = null, // function to call next page,
onMovePrev = null, // function to call previous page
}) => {
const [currentPage, setCurrentPage] = useState(startPage);
const canGoNext = total >= limit;
const canGoPrev = currentPage > 1;
function handleNext(e) {
if (canGoNext) {
setCurrentPage((prevState) => prevState+1);
onMoveNext && onMoveNext({ currentPage });
}
}
function handlePrev(e) {
if (canGoPrev) {
setCurrentPage((prevState) => prevState-1);
onMovePrev && onMovePrev({ currentPage });
}
}
return (
<div>
<button onClick={handlePrev} disabled={!canGoPrev}>Prev</button>
<button onClick={handleNext} disabled={!canGoNext}>Next</button>
</div>
);
};
Here is how you can use Paginator in other components.
const PER_PAGE = 30; // get max # of records per page
const Softwares = () => {
const [softwares, setSoftwares] = useState([]);
const [valid, setValid] = useState(false);
const onFetchData = ({ currentPage }) => {
fetch(`http://127.0.0.1:8000/api/software/?p=${currentPage}&per_page=${PER_PAGE}`)
.then(response => response.json())
.then(data => {
setSoftwares(data.results);
setValid(true);
})
}
useEffect(() => {
onFetchData({ currentPage: 1 })
}, []);
return (
<>
{softwares.map(s => (
<Article key={s.id} pathname={s.id} title={s.title} image={s.image} pubdate={s.pub_date} icon={s.category.parent.img} categoryID={s.category.id} categoryName={s.category.name} dCount={s.counter} content={s.content} />
))}
<Paginator total={softwares.length} limit={PER_PAGE} onMoveNext={onFetchData} onMovePrev={onFetchData} />
</>
);
};

React forwardRef inside a loop

I'm trying to use react forwardRef to call a function inside bunch of child components. Here is the code.
const WorkoutFeedbackForm = ({
latestGameplaySession,
activityFeedbacks,
selectedActivityIndex,
setIsReady,
}) => {
const [isLoading, setIsLoading] = useState(false);
const workoutRef = createRef();
const refMap = new Map();
const onSubmitFeedbackClick = useCallback(async () => {
setIsLoading(true);
await workoutRef.current.onSubmitFeedback();
for (let i = 0; i < activityFeedbacks.length; i++) {
const activityRef = refMap.get(activityFeedbacks[i].sessionID);
console.log(activityRef);
if (activityRef && activityRef.current) {
activityRef.current.onSubmitFeedback();
}
}
setIsLoading(false);
}, [
activityFeedbacks,
refMap,
]);
return (
<>
<FeedbackFormContainer
key={`${latestGameplaySession.id}-form`}
name="Workout Feedback"
feedback={latestGameplaySession.coachFeedback}
isSelected
gameplaySessionDoc={latestGameplaySession}
pathArr={[]}
ref={workoutRef}
/>
{activityFeedbacks.map((feedback, index) => {
const activityRef = createRef();
refMap.set(feedback.sessionID, activityRef);
return (
<FeedbackFormContainer
key={feedback.sessionID}
name={feedback.name}
feedback={feedback.coachFeedback}
isSelected={index === selectedActivityIndex}
gameplaySessionDoc={latestGameplaySession}
pathArr={feedback.pathArr}
setIsReady={setIsReady}
ref={activityRef}
/>
);
})}
<FeedbackSubmit
onClick={onSubmitFeedbackClick}
isLoading={isLoading}
>
Save Feedbacks
</FeedbackSubmit>
</>
);
};
The problem is it seems createRef only works for the component outside the loop. Do you have any idea what's wrong here. Or is it not possible to do that?

React setState hook is not working when trying to clear/empty/delete/set back to initial state

I have a clearState function which sets some useState hooks back to their initial state when the restart button is clicked. However, they say that my setState is not a function. Please check code below:
App.js
...
const [question, setQuestion] = useState(0);
const [response, setResponse] = useState({});
const [answer, setAnswer] = useState({});
const [answerId, setAnswerId] = useState({});
...
Modal.js
const Modal = ({
setResponse,
setAnswer,
setAnswerId,
setQuestion,
setAnswerNameArr,
}) => {
const [open, setOpen] = useState(false);
const clearState = () => {
setOpen(false); //works
setQuestion(0); //works
setAnswer({}); //does not work
setAnswerId({});
setResponse({});
setAnswerNameArr([]);
};
...
return (
<Modal
...
>
...
<Button
onClick={()=>handleSubmit()}
>
Restart
</Button>
</Modal>
);
};
export default Modal;
The error:
Uncaught TypeError: setAnswer is not a function
Thanks in advance.
It looks like you aren't passing your state setting hooks in to your <Modal> so they're not available.
It isn't a good idea to do that anyway, tbh. If you need a child to affect the state of a parent it would be better to pass a single call-back:
const Modal = ({
onSubmitCb
}) => {
const [open, setOpen] = useState(false);
const clearState = () => {
setOpen(false); //works
setQuestion(0); //works
onSubmitCb && onSubmitCb()
};
...
return (
<Modal>
...
<Button
onClick={()=>handleSubmit()}
>
Restart
</Button>
</Modal>
);
};
and in your parent:
const App = ()=>{
const clearState = () => {
setAnswer({});
setAnswerId({});
setResponse({});
setAnswerNameArr([]);
};
....
return {
<Modal ... onSubmitCb={clearState} />
}
}

Categories