I want to get all posts all at once to limit requests to firebase server and increase speed. however I only want the FlatList to render 20 posts at a time from all the data saved in state for performance. Basically get all posts from firebase but render 20 at a time on device without making a request every 20 posts and then when user scrolls to the bottom more posts copied from local state to local state and now user sees 40 posts. Is that possible. Please help
This is how I am getting all posts how do make logic to limit posts locally with FlatList:
const Posts = (props) => {
//getting all posts at once but want to limit them on device from two different states
// when user scrolls down the function adds 20 more posts
const [allPosts, setAllPosts] = useState();
const [loading, setLoading] = useState(true)
useEffect(() => {
getPosts();
}, []);
const getPosts = async () => {
try {
var all = [];
const unsubscribe = await firebase
.firestore()
.collection("Posts")
.orderBy("timestamp",'desc')
.get()
.then((querySnapshot) => {
querySnapshot.docs.forEach((doc) => {
all.push(doc.data());
});
setLoading(false);
});
setAllPosts(all);
if(currentUser === null){
unsubscribe()
}
} catch (err) {
setLoading(false);
}
};
}
You can use onEndReached prop from the FlatList component to call a function when the user reaches the end of the list. Then you have to enlarge your list in your state by 20.
Related
I have multiple users in my app, and I can login smoothly but when I logout and I login as second user I still can see the first user screens until I refresh my app then I can see the second user screen?
Here is my code:
const UserInbox = () => {
const [userData, setUserData] = useState([])
const message = db.collection('feedback').where('recipient','==',userData ?
auth.currentUser.email:'unknown')
{/*fetch only logged in user messages from database*/}
const fetchData = async()=>{
const unsubscribe = auth.onAuthStateChanged(async user => {
const list = [];
if(user){
await message
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
const {userId,recipient,messageText} = doc.data()
list.push({
id:doc.id,
userId,
recipient,
messageText
})
// doc.data() is never undefined for query doc snapshots
console.log(doc.data().messageText);
});
setUserData(list)
})
} else{
setUserData(null)
}
})
return () => unsubscribe();
}
I was trying to fix it but I couldn't figure it out because when I look at my code I don't see any errors but I feel I'm missing something? so please do anyone can help me out to display the right screen for the right logged in without refreshing the app with every single login.
So I'm currently brushing up my react skills and learning firebase. I found it simple enough to configure and now I have been successfully making fetch requests from my realtime database.
I am currently building some sort of a birthday app that would show monthly and daily celebrants, and have generated mock data that I imported into my database. Currently, the first names are being fetched successfully and displaying on the screen, however the images (that are hosted on another site and fetched as a string from the db) are not loading properly and I'm getting 500 error in my console. If I keep refreshing though, they eventually load.
I'm thinking it must be with the way I make my fetch request. I basically fetch all users and then make a filter (have not explored fetching with queries yet) so I thought it would work.
This is the code for the fetch requests.
export const getUsers = () => {
const usersDb = ref(database);
return get(child(usersDb, `/users`)).then((snapshot) => {
return snapshot.val();
});
};
export const getMonthlyCelebrants = async () => {
const users = await getUsers();
const monthlyCelebs = [];
for (let user in users) {
const userMonth = +users[user]["birth_date"].split("/")[1];
userMonth === getMonthNum() && monthlyCelebs.push(users[user]);
}
return monthlyCelebs;
};
And this is the Monthly Celebrants component I use them in:
export default function Monthly() {
const [monthlyCelebs, setMonthlyCelebs] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
async function loadPage() {
setIsLoading(true);
const users = await getMonthlyCelebrants();
setIsLoading(false);
setMonthlyCelebs(users);
}
loadPage();
}, []);
return (
<div>
<h2>{getMonthAndYear()} Celebrants</h2>
{isLoading ? (
<p>Celebrants loading...</p>
) : (
sortBirthdays(monthlyCelebs).map((monthlyCeleb) => {
return (
<SingleMonthlyCelebrant
monthlyCeleb={monthlyCeleb}
key={`${monthlyCeleb.birth_date}${monthlyCeleb.first_name}`}
/>
);
})
)}
</div>
);
}
Any tips or advice would be greatly appreciated. Thank you!
Hi guys hoping someone can help me with an issue. I built a function that fetches user posts from backend and returns them as a response:
const [posts, setPosts] = useState([]);
const newsFeed = async () => {
await axios
.post(`${process.env.REACT_APP_API}/news-feed`)
.then((res) => {
setPosts(res.data);
})
.catch((err) => {
console.log(err);
});
};
This function gets called by useEffect and also whenever a post is submitted, liked, commented on etc. so that users' posts are always re-rendered every time they are modified or added.
It was working fine until I decided to implement Infinite Scroll to my application. I installed the npm package react-infinite-scroll-component and modified my function to look like this:
const [posts, setPosts] = useState([]);
const [page, setPage] = useState(1);
const newsFeed = async () => {
await axios
.post(
`${process.env.REACT_APP_API}/news-feed/${page}`)
.then((res) => {
let newPosts = posts;
newPosts = newPosts.concat(res.data);
setPosts(newPosts);
setPage(page + 1);
})
.catch((err) => {
console.log(err);
});
};
The infinite scroll is working just fine but now the posts are not being re-rendered every time this function gets called and instead I need to refresh the page to see changes. I tried resetting the state of page back to 1 again on my postSubmit/likeHandler functions but this didn't have any effect. I'm not seeing any errors in the console so am unsure what is going on.
Replace let newPosts = posts; with let newPosts = [...posts]; Passing the same array reference to setState will not cause an update, since the value hasn't changed. By using [...posts], you are creating a new array, causing the component to update.
I'm building a web application that consumes TMDB Api. I have the following code that fetch all information about a TV Show
export const useShowInfoFetch = ({showId}) => {
const [data, setData] = useState({})
const [loading, setLoading] = useState(false)
const [_error, _setError] = useState(false)
const fetchShowInfo = useCallback(() => {
setLoading(true)
try {
axios.get(getShowInfo(showId))
.then(response => {
setData(response.data)
})
} catch (error) {
_setError(true)
} finally {
setLoading(false)
}
}, [showId])
useEffect(() => {
fetchShowInfo()
}, [fetchShowInfo])
return [data, loading, _error]
}
All the information fetched is displayed in page, that also has Links with react-router-dom. Those links goes to another tv show page.
The problem is that when I'm in a page with a tv show that has X amount of seasons and I click a tv show with less seasons, the seasons from the page I was are persisting for a little bit of time. So, when I fetch the information for each season I got a 404 in the page that has less seasons.
Here is a screenshot of the error
The orange circle is what it's displayed since I click the tv show with less seasons.
As you can see, the seasons from the previous page are persisting for a little time, and because The Alienist has only 2 seasons (not 9) I get the 404. You can also note that latter, the correct amount of seasons are displayed.
I've tried to add a cleanup method in the useEffect hook. Something like this:
useEffect(() => {
fetchShowInfo()
return function cleanup() {
setData({})
}
}, [fetchShowInfo])
But this did not work.
I know that I can handle that with a catch after the then Axios promise, but I want to figure out why this is happening and fix the issue with a good solution instead of avoiding it.
Any help is welcomed and I can share the repository with all the code if needed.
EDIT:
To display the similar movies I use another custom hook
export const useSimilarFetch = (elementType, elementId) => {
const [similarElements, setSimilarElements] = useState({elements: []})
const [similarLoading, setSimilarLoading] = useState(false)
const [_error, _setError] = useState(false)
const fetchSimilarElements = useCallback(async (endpoint) => {
console.log(">>> fetching similar elements <<<")
setSimilarLoading(true)
try {
await axios.get(endpoint)
.then(response => {
setSimilarElements(() => ({
elements: [...response.data.results],
currentPage: response.data.page,
totalPages: response.data.total_pages
}))
})
} catch (error) {
_setError(true)
} finally {
setSimilarLoading(false)
}
}, [])
useEffect(() => {
fetchSimilarElements(getSimilar(elementType, elementId));
}, [fetchSimilarElements, elementType, elementId])
return [{similarElements, similarLoading, _error}, fetchSimilarElements]
}
Then, in my ShowInfoComponent I call all the needed hooks like this:
const {showId} = useParams()
const [data, loading, _error] = useShowInfoFetch({showId})
const [{similarElements, similarLoading}] = useSimilarFetch("tv", showId)
Thanks.
By the time showId changes, data has to wait one additional render cycle, so showId is already used even though data has not yet been fetched. The UI relies on both showId and data, yet data depends on showId. One way to solve this could be having your UI to rely on data alone. What about the id? Add it to data for example. We merely want to avoid the desynchronization.
Something like this:
export const useShowInfoFetch = ({showId}) => {
const [data, setData] = useState({})
const [loading, setLoading] = useState(false)
const [_error, _setError] = useState(false)
const fetchShowInfo = useCallback(() => {
setLoading(true)
try {
axios.get(getShowInfo(showId))
.then(response => {
setData({ id: showId, info: response.data})
})
} catch (error) {
_setError(true)
} finally {
setLoading(false)
}
}, [showId])
useEffect(() => {
fetchShowInfo()
}, [fetchShowInfo])
return [data, loading, _error]
}
Then use data.id to build your links.
If response.data already contains the id, then even better, use that.
That's just an example, of course but hopefully you get the idea.
I might be wrong but I believe you are not watching the correct value on the useEffect. You should be watching showId and not the function fetchShowInfo. That is:
useEffect(() => {
fetchShowInfo()
}, [showId]) --> HERE
And as you are memoized the callback, if you are watching the wrong variable then you will get back the 'last answered'.
We have a ReactJs application which used Firebase. When you first register for the application and you log in for the first time, the app loads a document from Firebase using the onSnapshot method, and renders a component that uses the data in the view. The problem is, when we make changes to the loaded document and those changes are written back to Firebase, the ReactJs application isn't picking up that those changes have been applied and so the view isn't updated. If however, we refresh the app, the changes we've made are rendered and future changes are picked up as normal and everything is fine.
Here is the code where we are calling the onSnapshot method. You can see I've stuck some debugging text in there, this is only rendered once when the document is not loaded, so I know that the problem is that this isn't picking up the document changes in firestore.
function useHub(hubId) {
console.debug("useHub");
const Firebase = React.useContext(FirebaseContext);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(false);
const [hub, setHub] = React.useState(null);
React.useEffect(() => {
console.debug("useHub: useEffect");
setHub(null);
console.log('****************************');
console.log("Hub ID " + hubId);
if (hubId) {
const unsubscribe = Firebase.firestore()
.collection("hubs")
.doc(hubId)
.onSnapshot(
(doc) => {
const hubDoc = Object.assign({}, { id: doc.id }, doc.data());
console.log('***************************');
console.log('***************************');
console.log(hubDoc);
console.log('***************************');
console.log('***************************');
setHub(hubDoc);
setLoading(false);
setError(false);
},
(error) => {
console.error("Error:", error);
setError(error.message);
setLoading(false);
}
);
return () => unsubscribe();
}
}, [Firebase, hubId]);
return {
loading,
error,
hub,
};
}
export { useHub };
What are the possible reasons why this would be occuring?
You may consider to have your component update itself as a workaround. I have found this document here, which describes the details.