I'm creating a simple app that queries an api that returns a list of books when you search by title. I am adding the option to sort the search results.
When a user searches for a title, eg 'harry potter', the app stores an array of books in state and renders the results.
When a user then selects the option to sort (eg by title), my array of books in state is correctly being sorted and updated, but the old unsorted books are the ones shown.
Notably, if I then select to sort by date, this time I am seeing sorted books being displayed by it's the books sorted by title not date!
And also, at all times, the value of my books array is correctly sorted, but the books being displayed are not (it's essentially the previous state of books which is being rendered).
This is observable if you keep switching between title and sort, the app will update, but the results will be sorted by title when you try to sort by date and vice versa.
It's almost as if when I select a sort option, the dom is rerendering, and then I'm changing the value of books in state, but that isn't then again causing a re-rendering of the dom.
Any ideas what might be going wrong?
sandbox
The code:
export default function App() {
const [input, setInput] = useState("");
const [books, setBooks] = useState([]);
const [loading, setLoading] = useState(false);
const [sort, setSort] = useState("");
const API_URL = `https://openlibrary.org/search.json?title=`;
const sortFn = (books, sortType) => {
setLoading(true);
if (sortType === "title") {
console.log("sorting by title!");
const sortedBooks = books.sort((a, b) => a.title.localeCompare(b.title));
setBooks(sortedBooks);
} else if (sortType === "publishDate") {
console.log("sorting by date, most recent");
const sortedBooks = books.sort((a, b) => {
const dateA = new Date(a.publishDate);
const dateB = new Date(b.publishDate);
return dateB - dateA;
});
console.log("sorted books:", sortedBooks);
setBooks(sortedBooks);
}
setLoading(false);
};
const getBooks = async (queryStr) => {
setLoading(true);
try {
const {
data: { docs }
} = await axios.get(`${API_URL}${queryStr}`);
// console.log(docs);
const slice = docs.slice(0, 10);
const results = slice.map((item) => ({
title: item.title,
author: item.author_name[0],
isbn: item.isbn[0].trim(),
publishDate: item.publish_date[0]
}));
if (sort) {
sortFn(results, sort);
} else {
setBooks(results);
}
setLoading(false);
} catch (err) {
console.log(err);
}
};
useEffect(() => {
if (books && sort) sortFn(books, sort);
}, [sort]);
const changeHandler = (e) => {
setInput(e.target.value);
};
const selectChange = (e) => {
setSort(e.target.value);
};
const submitHandler = (e) => {
e.preventDefault();
if (input) {
//must replace whitespace within string with '+' symbol
const query = input.trim().replace(" ", "+");
getBooks(query);
}
setInput("");
};
console.log("books:", books);
const tiles = books.map((book) => (
<Book
key={book.isbn}
title={book.title}
author={book.author}
publishDate={book.publishDate}
imgURL={`https://covers.openlibrary.org/b/isbn/${book.isbn}-M.jpg`}
/>
));
return (
<div className="App">
<form onSubmit={submitHandler}>
<input type="text" value={input} onChange={changeHandler} />
<button type="submit">Submit</button>
</form>
<select onChange={selectChange}>
<option value="">Sort by</option>
<option value="title">Title(A-Z)</option>
<option value="publishDate">Publish Date</option>
</select>
{loading && <div>Loading</div>}
{!loading && books ? tiles : null}
</div>
);
}
Related
My sorting is working but sometimes my data doesnot change as I select the option no change occurs , I guess I am not using useEffect correctly So I want what am I doing wrong , I am very confused
const { data: property, isFetching, refetch } = useQuery(['PropertySearch', startDate, endDate, where, adultCount, childCount], propertyAvaibility, {
retry: false,
enabled: !!data
})
useEffect(() => {
const sortArray = type => {
const types = {
number_of_bars: 'number_of_bars',
starting_price: 'starting_price',
};
const sortProperty = types[type];
const sorted = property?.sort((a, b) => b[sortProperty] - a[sortProperty]);
setData(sorted);
};
sortArray(sortType);
}, [sortType]);
<select onChange={(e) => setSortType(e.target.value)} className="form-control">
<option value="number_of_bars">number_of_bars</option>
<option value="starting_price">starting_price</option>
</select>
{
property?.map((item) => (
<PropertyCard
key={item?.id}
title={item?.title}
image={item?.cover_image?.url}
location={item.address}
displayFooter={false}
displayButton={false}
rating={true}
item={item}
type={item?.brand_name}
link="property">
{item?.description?.slice(0, 150)}
</PropertyCard>
))
}
I think your problem is you're using property?.map which is always referred to your original list.
For a possible fix, you could modify it to data?.map which is your sorted list.
And you also need to set a default value for data
const [data, setData] = useState(property); //`property` is from your fetched data
Full change can be
const { data: property, isFetching, refetch } = useQuery(['PropertySearch', startDate, endDate, where, adultCount, childCount], propertyAvaibility, {
retry: false,
enabled: !!data
})
const [data, setData] = useState(property); //set `property` as your default data
const [sortType, setSortType] = useState('rating');
useEffect(() => {
const sortArray = type => {
const types = {
number_of_bars: 'number_of_bars',
starting_price: 'starting_price',
};
const sortProperty = types[type];
const sorted = property?.sort((a, b) => b[sortProperty] - a[sortProperty]);
setData(sorted);
};
sortArray(sortType);
}, [sortType]);
const displayedData = data?.length ? data : property //check if data is available
<select onChange={(e) => setSortType(e.target.value)} className="form-control">
<option value="number_of_bars">number_of_bars</option>
<option value="starting_price">starting_price</option>
</select>
{
displayedData?.map((item) => ( //the main change is here
<PropertyCard
key={item?.id}
title={item?.title}
image={item?.cover_image?.url}
location={item.address}
displayFooter={false}
displayButton={false}
rating={true}
item={item}
type={item?.brand_name}
link="property">
{item?.description?.slice(0, 150)}
</PropertyCard>
))
}
i am trying to make a filter feature for a website i am working on, i am using an html range slider. The problem is that the values update just if they are going down, for example if i set the slider to $500, only the products that cost $500 or less will appear, if i set the value lower, it's going to work how is supposed to work, but if i try to set the value bigger, the items will not filter, for example, the value is set to $500, if set the value to $600 only the items that are $500 or less will render, but not the $600 ones.
here is my code:
const Shop = () => {
const [sliderValue, setValue] = useState(0);
const [filterItems, setApplyFilter] = useState(false);
const [newData, setData] = useState(data);
const checkChange = () => {
if (sliderValue > 3) {
setApplyFilter(true);
} else {
setApplyFilter(false);
}
console.log(applyFilter);
};
const applyFilter = () => {
if (filterItems === true) {
const filteredData = newData.filter((item) => item.price <= sliderValue);
console.log(filteredData);
setData(filteredData);
} else {
setData(data);
}
};
useEffect(() => {
checkChange();
applyFilter();
}, [sliderValue]);
const handleChange = (value) => {
setValue(value);
};
return (
<div className="slider-container">
<input
type="range"
min={0}
max={1000}
value={sliderValue}
onChange={(e) => handleChange(e.target.value)}
className="slider"
/>
</div>
);
}
The problem: you are changing the data with setData(), so every time you move your scrollbar this deletes some data. If you want to keep a constant information that is available to all your application, consider using useRef(). This creates a persistent object for the full lifetime of the component.
import { useRef } from 'react'
const Shop = () => {
const dataArr = useRef(data)
...
const applyFilter = () => {
if (filterItems === true) {
// Access with "current" attribute
const filteredData = dataArr.current
.filter((item) => item.price <= sliderValue);
setData(filteredData);
}
}
}
Working example
I think it's something to do with this two lines:
const filteredData = newData.filter((item) => item.price <= sliderValue);
setData(filteredData);
Once you have filtered your data once, the value of newData in your state will be only the already filtered data.
Let's say we start with prices: newData=[100, 200, 300, 400]
We filter it for the first time down to 200, so now newData=[100, 200]
Next we filter up to 300, but newData only has [100, 200]
So just change those two lines for:
const filteredData = data.filter((item) => item.price <= sliderValue);
setData(filteredData);
This is asuming you have a variable data declared or imported somewhere with the comple data set.
You don't need state for data array since it can be determined on every render based on some other state.
const Shop = ({ inputData }) => {
const [sliderValue, setValue] = useState(0);
// This flag is deterministic based on sliderValue, so determine it here
const filterItems = sliderValue > 3;
// The items that will make it past the filter are deterministic, based on your filterItems flag
// so no state is necessary
const renderItems = filterItems ? inputData.filter(i => i.price <= sliderValue) : inputData;
const handleChange = (value) => {
setValue(value);
};
return ...
};
I am stuck on this for some reason. I know how to use .sort when there is a simple array. I am not quite sure how to sort a nested object in an array using a variable in that object. I can sort it, but I am not sure how to display it.
Here is what I am working with. I get data from a database and map over that data to display it. Everything works as expected. Now I want to take that data and sort it by artist.
Here is the code I am working with.
export default function ShowRecords() {
const classes = recordFormStyles();
const url = " http://localhost:5000";
//get userData state to use in useEffect
//set state for showing records in database and opening/closing modals
const [newRecords, newRecordData] = React.useState([]);
const [editOpen, handleEditModal] = React.useState(false);
const [addModalOpen, handleAddModal] = React.useState(false);
//set state for edit records
const [title, setTitle] = React.useState("");
const [artist, setArtist] = React.useState("");
const [rating, setRating] = React.useState("");
const [genre, setGenre] = React.useState("");
const [description, setDescription] = React.useState("");
const [userId, setUserId] = React.useState("");
//set state for favorite icon
const [favorite, setFavorite] = React.useState([]);
const fetchFavoriteData = async () => {
const result = await axios.get(url + "/favorite/get", authToken);
setFavorite(result.data);
};
const addFavorites = async (_id, title, artist, rating, genre, description, isFavorite) => {
const favorites = {
userId: _id,
title,
artist,
rating,
genre,
description,
isFavorite
};
const result = await axios.post(
url + "/favorite/add",
favorites,
authToken
);
setFavorite(result.data);
};
const deleteFavorite = async (title) => {
await axios.delete("http://localhost:5000/favorite/delete", {
data: { title: title },
authToken,
});
};
//functions to control state
const handleAddModalOpen = () => {
handleAddModal(true);
};
const handleCloseAddModal = () => {
handleAddModal(false);
};
const handleIsEditModalClose = () => {
handleEditModal();
};
//fetch record data
const fetchData = async () => {
const result = await axios.get(url + "/record/get", authToken);
newRecordData(result.data);
};
React.useEffect(() => {
fetchData();
fetchFavoriteData();
}, []);
// delete records
const deleteRecord = async (_id) => {
const deleteRecords = {
_id: _id,
};
await axios.delete(url + "/record/" + _id, deleteRecords).then((result) => {
const refresh = newRecords.filter((result) => result._id !== _id);
newRecordData(refresh);
});
};
//functions for controlling edit record state
const editRecord = (_id, title, artist, rating, genre, description) => {
setUserId(_id);
setTitle(title);
setArtist(artist);
setRating(rating);
setGenre(genre);
setDescription(description);
handleEditModal(true);
console.log(title);
};
//functions for setting favorite state and color and post request to add favorite
return (
<div>
{/* set props */}
<Favorites />
<AddRecord
isAddModalOpen={addModalOpen}
handleIsAddModalClose={handleCloseAddModal}
addNewRecords={newRecords}
handleIsAddModalOpen={handleAddModal}
refreshRecordData={newRecordData}
/>
<EditRecords
editModalOpen={editOpen}
handleCloseEditModal={handleIsEditModalClose}
editUserId={userId}
editTitle={title}
editArtist={artist}
editRating={rating}
editGenre={genre}
editDescription={description}
editTitleState={setTitle}
editArtistState={setArtist}
editRatingState={setRating}
editGenreState={setGenre}
editDescriptionState={setDescription}
editUrl={url}
editFetchData={fetchData}
editNewRecordData={newRecordData}
/>
<Button
className={classes.addButton}
onClick={() => handleAddModalOpen(true)}
>
Add Record
</Button>
<div className={classes.cardsContainer}>
<Grid container spacing={8} style={{ padding: 80 }} justify = "center">
{newRecords.length > 0 &&
newRecords.map((element) => (
<RecordCard
key = {element._id}
element={element}
editRecord={editRecord}
deleteRecord={deleteRecord}
addFavorites = {addFavorites}
deleteFavorite = {deleteFavorite}
favorite = {favorite}
/>
))}
</Grid>
</div>
</div>
);
}
I get the data in my uesEffect and I want to sort it using the Arist name. I am just unsure on how to do that. I couldn't find much googling.
Sort the data before you save it into state. The sort function can take in a function that returns -1, 0, 1 to determine how things should be ordered. The below example uses the localeCompare function to sort by the artist.
let data = [
{ artist: 'john', record: '1' },
{ artist: 'mary', record: '2' },
{ artist: 'bob', record: '3' }
];
let sorted = data.sort((a,b) => (a.artist.localeCompare(b.artist)));
console.log(sorted);
import React, { useState, useEffect } from "react";
import axios from "axios";
const App = () => {
let [countries, setCountries] = useState([]);
const [newCountry, newStuff] = useState("");
const hook = () => {
//console.log("effect");
axios.get("https://restcountries.eu/rest/v2/all").then((response) => {
console.log("promise fulfilled");
setCountries(response.data);
//console.log(response.data);
});
};
const filter = (event) => {
newStuff(event.target.value);
if (event.target.value === undefined) {
return
} else {
let value = event.target.value;
console.log(value);
countries = countries.filter((country) => country.name.startsWith(value));
setCountries(countries);
console.log(countries);
}
};
useEffect(hook, []);
return (
<div>
<p>find countries</p>
<input value={newCountry} onChange={filter} />
<ul>
{countries.map((country) => (
<li key={country.name.length}>{country.name}</li>
))}
</ul>
</div>
);
};
export default App;
So I have a search bar so that when you enter a few characters it will update the state and show the countries that start with the respective first characters. However, nothing is being shown when I enter input into my search bar. Also, my filter function, when I console.log my countries array which is supposed to have the countries that start with the characters I entered, it's always an empty array.
You need some changes in order to make this work:
Use two states for countries, one for the list you
get in the initial render and another for the current filter
countries.
const [countriesStore, setCountriesStore] = useState([]); // this only change in the first render
const [countries, setCountries] = useState([]); // use this to print the list
I recomed to use any tool to manage the state and create a model for
the countries ther you can make the side effect there and create an
action that update the countries store. I'm using Easy Peasy in
my current project and it goes very well.
Take care of the filter method because startsWith
method is not case-insensitive. You need a regular expression or
turn the current country value to lower case. I recommend to use
includes method to match seconds names like island in the search.
const filterCountries = countriesStore.filter(country => {
return country.name.toLowerCase().includes(value);
});
Remove the if condition in the filter in order to include the
delete action in the search and get the full list again if
everything is removed.
Just in the case, empty the search string state in the first
render
useEffect(() => {
hook();
setSearchString("");
}, []);
Replace the length in the list key. You can use the name and trim to remove space.
<li key={country.name.trim()}>{country.name}</li>
The final code look like this:
export default function App() {
const [countriesStore, setCountriesStore] = useState([]);
const [countries, setCountries] = useState([]);
const [searchString, setSearchString] = useState("");
const hook = () => {
axios.get("https://restcountries.eu/rest/v2/all").then(response => {
console.log("promise fulfilled");
setCountriesStore(response.data);
setCountries(response.data);
});
};
const filter = event => {
setSearchString(event.target.value);
let value = event.target.value;
const filterCountries = countriesStore.filter(country => {
return country.name.toLowerCase().includes(value);
});
setCountries(filterCountries);
};
useEffect(() => {
hook();
setSearchString("");
}, []);
return (
<div>
<p>find countries</p>
<input value={searchString} onChange={filter} />
<ul>
{countries.map(country => (
<li key={country.name.trim()}>{country.name}</li>
))}
</ul>
</div>
);
}
You need to wrap your hook into async useCallback:
const hook = useCallback(async () => {
const {data} = await axios.get("https://restcountries.eu/rest/v2/all");
setCountries(data);
}, []);
you are not able to mutate state countries. Use immutable way to update your state:
const filter = (event) => {
newStuff(event.target.value);
if (event.target.value === undefined) {
return
} else {
let value = event.target.value;
setCountries(countries.filter((country) => country.name.startsWith(value)));
}
};
And useState is asynchronous function. You will not see result immediately. Just try to console.log outside of any function.
I'm working with Firebase - Cloud Firestore and at the moment I would like to paginate all the records available. I already have a list of records and what is left is some pagination for this. I'm new with Cloud Firestore, so any clarity is appreciated.
I checked the Firestore documentation (https://firebase.google.com/docs/firestore/query-data/query-cursors#paginate_a_query) and examples with ReactJS, but there is not much available.
I understand that eg:.startAt(0), .limit(10), but the question is how to paginate properly with this component called at the render method.
import React, { Component } from 'react';
import Pagination from "react-js-pagination";
import firestore from "./Firebase";
export default class DataList extends Component {
constructor(props) {
super(props);
this.state = {
dbItems: [],
currentPage: 1,
itemsPerPage: 3,
totalItemCount: 1,
activePage: 15
}
this.handlePageChange = this.handlePageChange.bind(this);
}
handlePageChange(pageNumber) {
console.log(`active page is ${pageNumber}`);
this.setState({ activePage: pageNumber });
}
async getItems() {
const { currentPage, itemsPerPage } = this.state;
const startAt = currentPage * itemsPerPage - itemsPerPage;
const usersQuery = firestore.collection('Users').orderBy("email").startAt(startAt).limit(itemsPerPage)
const snapshot = await usersQuery.get()
const items = snapshot.docs.map(doc => doc.data())
return this.setState({
dbItems: items,
totalItemCount: firestore.collection('Users').get().then(res => console.log(res.size))
})
}
componentDidMount() {
this.getItems()
}
componentDidUpdate(prevProps, prevState) {
const isDifferentPage = this.state.currentPage !== prevState.currentPage
if (isDifferentPage) this.getItems()
}
render() {
return (
<div>
{this.state.dbItems.map((users, index) => {
return (
<p key={index}>
<b>First Name:</b> {users.firstname} <br />
<b>Email:</b> {users.email}
</p>
)
})
}
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={this.state.itemsPerPage}
totalItemsCount={this.state.totalItemCount}
pageRangeDisplayed={this.state.itemsPerPage}
onChange={this.handlePageChange}
/>
</div>
)
}
}
Thank you for the help!
Pagination can be achieved using startAt()
// Get Items.
async fetchUsers = () => {
// State.
const {users, usersPerPage} = this.state
// Last Visible.
const lastVisible = users && users.docs[users.docs.length - 1]
// Query.
const query = firestore.collection('Users')
.orderBy('email')
.startAfter(lastVisible)
.limit(usersPerPage)
// Users.
const users = await query.get()
// ..
return this.setState({users})
}
// Did Mount.
componentDidMount() {
this.fetchUsers()
}
// Did Update.
componentDidUpdate(prevProps, prevState) {
const isDifferentPage = this.state.currentPage !== prevState.currentPage
if (isDifferentPage) this.fetchUsers()
}
Anyone new to Firestore and Firestore Pagination with ReactJS that would be kinda confusing to understand how Pagination will work or when to trigger call to next set of documents in firestore. anyone struggle like this try my example to make some ideas and process ahead.(Im using React-Bootstrap to render UI Elements)
01 - Install Package react-infinite-scroll-component
First Install this package yarn add react-infinite-scroll-component
02 - Include Package
Include it to your file by 'import InfiniteScroll from 'react-infinite-scroll-component';' importing it
03 - Init State
initiate state with empty list array
this.state = {
list: [],
};
04 - Create Function to get first set of data and initiate it with component did mount
//component did mount will fetch first data from firestore
componentDidMount(){
this.getUsers()
}
getUsers(){
let set = this
//initiate first set
var first = set.ref.collection("users").limit(12);
first.get().then(function (documentSnapshots) {
// Get the last visible document
var lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1];
//initiate local list
const list = [];
documentSnapshots.forEach(function(doc) {
//im fetching only name and avatar url you can get any data
//from your firestore as you like
const { name, avatar_full_url } = doc.data();
//pushing it to local array
list.push({ key: doc.id, name, avatar_full_url });
});
//set state with updated array of data
//also save last fetched data in state
set.setState({ list, last: lastVisible });
});
}
05 - Create function to get balance data set
fetchMoreData = () => {
let set = this
//get last state we added from getUsers()
let last = this.state.last
var next = set.ref.collection("users").startAfter(last).limit(12);
next.get().then(function (documentSnapshots) {
// Get the last visible document
var lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1];
const list = [];
documentSnapshots.forEach(function(doc) {
//im fetching only name and avatar url you can get any data
//from your firestore as you like
const { name, avatar_full_url } = doc.data();
list.push({ key: doc.id, name, avatar_full_url });
});
//set state with updated array of data
//also save last fetched data in state
let updated_list = set.state.list.concat(list);
set.setState({ list: updated_list, last: lastVisible });
});
};
06 - Render UI
<InfiniteScroll
dataLength={this.state.list.length}
next={this.fetchMoreData}
hasMore={true}
loader={<span className="text-secondary">loading</span>}>
<Row className="mt-3">
{ this.state.list.map((single, index) => (
<Col lg={4} key={ index }>
<div>
<Image src={ single.avatar_full_url }roundedCircle width="100" />
<h2>{ single.name }</h2>
</div>
</Col>
))}
</Row>
</InfiniteScroll>
Check this example this could help anyone who trying previous / next pagination
//initial state
const [list, setList] = useState([]);
const [page, setPage] = useState(1);
//loading initial data
useEffect(() => {
const fetchData = async () => {
await firebase.firestore().collection('users')
.orderBy('created', 'desc') //order using firestore timestamp
.limit(5) //change limit value as your need
.onSnapshot(function(querySnapshot) {
var items = [];
querySnapshot.forEach(function(doc) {
items.push({ key: doc.id, ...doc.data() });
});
setList(items);
})
};
fetchData();
}, []);
After loading initial data use following function for next button trigger
//next button function
const showNext = ({ item }) => {
if(list.length === 0) {
//use this to show hide buttons if there is no records
} else {
const fetchNextData = async () => {
await firebase.firestore().collection('users')
.orderBy('created', 'desc') //order using firestore timestamp
.limit(5) //change limit value as your need
.startAfter(item.created) //we pass props item's first created timestamp to do start after you can change as per your wish
.onSnapshot(function(querySnapshot) {
const items = [];
querySnapshot.forEach(function(doc) {
items.push({ key: doc.id, ...doc.data() });
});
setList(items);
setPage(page + 1) //in case you like to show current page number you can use this
})
};
fetchNextData();
}
};
Then Previous button function
//previous button function
const showPrevious = ({item}) => {
const fetchPreviousData = async () => {
await firebase.firestore().collection('users')
.orderBy('created', 'desc')
.endBefore(item.created) //this is important when we go back
.limitToLast(5) //this is important when we go back
.onSnapshot(function(querySnapshot) {
const items = [];
querySnapshot.forEach(function(doc) {
items.push({ key: doc.id, ...doc.data() });
});
setList(items);
setPage(page - 1)
})
};
fetchPreviousData();
};
at the end create list view & two buttons like this
{
//list doc's here this will come inside return (place this code inside table)
list.map((doc) => (
<tr key={doc.key}>
<td>{ doc.name }</td>
<td>{ doc.age }</td>
<td>{ doc.note }</td>
</tr>
))
}
{
//show previous button only when we have items
//pass first item to showPrevious function
page === 1 ? '' :
<Button onClick={() => showPrevious({ item: list[0] }) }>Previous</Button>
}
{
//show next button only when we have items
//pass last item to showNext function
list.length < 5 ? '' :
<Button onClick={() => showNext({ item: list[list.length - 1] })}>Next</Button>
}
That's it check my code comments where you can change as per your need. this is what happens when you paginate using Firebase FireStore. you can use create custom hook to reuse these component as per your need.
Hope this could help someone so i made a gist check it here
here AddTable and AddForm is adding table and add form to fill data in table...
import React, { useEffect, useState } from "react";
import Button from "react-bootstrap/Button";
import Pagination from "react-bootstrap/Pagination";
import AddTable from "../management/AddTable";
import AddForm from "../management/AddSuperAdminForm";
import {
where,
getDocs,
collection,
query,
orderBy,
startAfter,
limit,
endBefore,
limitToLast,
} from "firebase/firestore";
import { db_firestore } from "../../../firebase.config";
const SuperAdmin = () => {
const [tableDataArray, setTableDataArray] = useState();
const [show, setShow] = useState(false);
const [editId, setEditId] = useState("");
const [oldUid, setOldUid] = useState("");
const [lastVisible, setLastVisible] = useState();
const [prevVisible, setPrevVisible] = useState();
const handleClose = () => {
setShow(false);
setEditId("");
};
const handleShow = () => {
setShow(true);
setEditId("");
};
let tempdata;
let pageSize = 3;
let q = query(
collection(db_firestore, "users"),
where("role", "==", "superadmin"),
orderBy("timestamps", "desc"),
limit(pageSize)
);
function nextPage(lastVisible) {
q = query(
collection(db_firestore, "users"),
where("role", "==", "superadmin"),
orderBy("timestamps", "desc"),
startAfter(lastVisible),
limit(pageSize)
);
}
function prevPage(firstVisible) {
q = query(
collection(db_firestore, "users"),
where("role", "==", "superadmin"),
orderBy("timestamps", "desc"),
endBefore(firstVisible),
limitToLast(pageSize + 1)
);
}
const newfun = async () => {
const querySnapshot = await getDocs(q);
tempdata = [];
// Get the last visible document
setLastVisible(querySnapshot.docs[querySnapshot.docs.length - 1]);
// Get the prev visible document
setPrevVisible(querySnapshot.docs[0]);
querySnapshot.forEach((doc) => {
const { name, email, uid } = doc.data();
tempdata.push([name, email, uid, doc.id]);
});
console.log("SuperAdmin...");
setTableDataArray(tempdata);
};
useEffect(() => {
newfun();
// setInterval(() => { // if you want to get new update after some secound
// newfun();
// }, 10000);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div>
<Button
className="d-block mx-auto my-2"
variant="primary"
onClick={handleShow}
>
Add SuperAdmin
</Button>
{/* -----> AddTable <------
Index will generate Automatic In Table.
Always keep action end of the table.
*/}
{tableDataArray ? (
<AddTable
tableHeaders={["Name", "Email", "uid", "Action"]}
tableData={tableDataArray}
fetchNew={newfun}
setEditId={setEditId}
setShow={setShow}
setOldUid={setOldUid}
/>
) : (
""
)}
<AddForm
fetchNew={newfun}
show={show}
setShow={setShow}
handleClose={handleClose}
editId={editId}
oldUid={oldUid}
/>
<Pagination className="float-end">
<Pagination.Item
className="shadow-none"
size="lg"
onClick={() => {
prevPage(prevVisible);
newfun();
}}
>
Previous
</Pagination.Item>
<Pagination.Item
className="shadow-none"
size="lg"
onClick={() => {
nextPage(lastVisible);
newfun();
}}
>
Next
</Pagination.Item>
</Pagination>
</div>
);
};
export default SuperAdmin;
Use startAt() or startAfter() for that
firestore
.collection("Users")
.startAt(0)
.limit(10)
.get()