can we run arrow function without useEffect()? - firebase

I have a function loadListings() in react native app it gets data from real time firebase database and renders it on on page in <Flatlist />
function..
let data = [];
listingRef.orderByChild("created_at").on("value", (snapshot) => {
data = [];
snapshot.forEach((listing) => {
data.push(listing.val());
});
setListings(data);
setLoading(false);
});
};
and finally
useEffect(() => {
loadListings();
}, []);
Can I use it without the effect hook?

Yes you can. Based on official documentation for useEffect hook the [] empty array at the end means what loadListings function will be executed ones when this component will be mounted.

Related

Caching into subcomponents

I'm building a site in nextjs but I came across a problem.
I have the cover of the site, where there is a list of products, and on the top menu the list of product categories.
The products are looking via getStaticProps (So that it is done by the servideor and is cached).
However, the categories are inside a separate component, where inside I need to load the category listing from my API.
getStaticProps does not work in this case as it is not a page but a component.
Fetching inside a useEffect is bad, as each access loads the api.
So the question remains, how can I do this server-side fetch and deliver the cached (json) return? (Simulating getStaticProps)
As your component is listed on every page, you could consider either using Context or local caching in the browser within the shared Category component.
Context provides a way to pass data through the component tree without
having to pass props down manually at every level.
But there are performance considerations using Context and may be overkill here. If you really don't want to hit the API, data is not changing often, and you don't need to pass functions through the component tree, then you could consider some basic browser caching using localStorage.
import React, { useState, useEffect } from 'react';
const mockAPI = () => {
return new Promise((resolve) => {
setTimeout(() => {
return resolve([
{
id: '1',
name: 'One'
},
{
id: '2',
name: 'Two'
}
]);
}, 1000);
});
};
const Component = () => {
const [categories, setCategories] = useState(null);
useEffect(() => {
(async () => {
if (categories === null) {
let data = window.localStorage.getItem('categories');
if (data === null) {
console.info('calling api...');
data = await mockAPI();
window.localStorage.setItem('categories', JSON.stringify(data));
}
setCategories(JSON.parse(data));
}
})();
}, []);
return <nav>{categories && categories.map((category) => <li key={category.id}>{category.name}</li>)}</nav>;
};
export default Component;
The caveat here is you need to know where to clear localStorage. There are many ways to implement this from using basic timers to looking at SWR
You could also consider Redux but is probably overkill for something elementary like this.

React Native state console.log

I am using firebase for my app and the data i read i want to put that in state to use it in different places.
it kinda works but when i want to console.log the state it updates like 30 times a second, am i doing something wrong?
this is my code
const db = firebase.firestore();
const [PBS1Detail, setPBS1Detail] = useState();
db.collection('Track').get().then((snapshot) => {
snapshot.docs.forEach(doc => {
renderTracks(doc)
}
)
});
const renderTracks = (doc) => {
let data = doc.data().data[0].Module;
return setPBS1Detail(data);
}
console.log(PBS1Detail)
i already tried to store it in a variable instead of state but thats not working for me, i can't get the variable from the function global
i am a noob i get it xd
You don't need a return statement when setting state. Also, it looks like you're performing some async function which means that when your component renders for the first time, PBS1Detail will be undefined since the db is a pending Promise.
You could/should put your async functions in useEffect hook:
useEffect(()=> {
db.collection('Track').get().then((snapshot) => {
snapshot.docs.forEach(doc => {
renderTracks(doc)
}
)
});
}, [])
const renderTracks = (doc) => {
let data = doc.data().data[0].Module;
setPBS1Detail(data);
}
Finally, your renderTracks function doesn't seem correct as it appears you're looping over docs and resetting your state each time.
Instead, maybe consider having an array for PBS1Detail
const [PBS1Detail, setPBS1Detail] = useState([]);
Then modify your async call:
useEffect(()=> {
db.collection('Track').get().then((snapshot) => {
let results = []
snapshot.docs.forEach(doc => {
results.push(renderTracks(doc))
}
)
setPBS1Detail(results)
});
}, [])
const renderTracks = (doc) => {
return doc.data().data[0].Module;
}
This way you're only setting state once and thus avoiding unnecessary re-renders and you're saving all of your docs instead of overwriting them.

react native data show on console.log but not on screen

Sometimes the app working well.
but I dont know why, sometimes the data from firebase give me blank screen instead of the data.
after I reopen the app it work.
for example,
one of my pages:
useEffect( () => {
const subscriber = firestore()
.collection('Trails')
.onSnapshot(querySnapshot => { //const querySnapshot = await firebase.firestore().collection('users').get();
const trails = [];
console.log('subscribe')
if (querySnapshot)
querySnapshot.forEach(async documentSnapshot => {
trails.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
console.log("trails test", trails)
});
setTrails(trails);
setLoading(false);
});
return () => {subscriber()};
}, []);
I made useEffect to get the data from DB then show it, and same - sometimes give me blank and sometimes works well.
I want to publish the app but im not satisfying with this bug?
sorry for my English, I dont even know how to describe this problem better.
Please can anyone guide me through? maybe my useEffect not doing well?
I think you should use debugging.
React native documentation
Stackoverflow question
I think there's issue with the return in useEffect return is called when componeent unmounts. here's an example how i handle async data fetching:
...
const [data, setData] = useState([]);
const isCurrentView = useRef(true);
useEffect(() => {
if (isCurrentView.current === true) {
const asyncFetch = async () => {
const response = await fetch(...something) //here some Asynchronous Call Like(axios, fetch)
setData([...response]);
};
asyncFetch();
}
return () => {
isCurrentView.current = false;
};
}, []);
...
im not 100% sure if this is the VERY best approach, but i have seen similar code in places so i addopted this.
problem was solved:
the setTrails was under scope and it kept refreshing with empty data.
querySnapshot.forEach(async documentSnapshot => {
trails.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
setTrails(trails); // <<<~~~~ put the set in this scope.
});
setLoading(false);
});

How to use redux hooks in getInitialProps

I have been trying to persist my redux store through a reload. I was using useEffect to dispatch my actions at first but then when I tried to reload the page router became undefined and I got a 500 error. After that I tried using getInitialProps and use the ctx.query.id but I ran into another error saying that hooks can only be called inside of the body of a function component.
How do I make it so hooks work inside of getInitialProps and what is the best way of persisting my store data through a reload?
export default function CarPage() {
const dispatch = useDispatch()
const router = useRouter()
const car = useSelector((state) => state.cars.car)
/*
useEffect(() => {
if(!car && router) {
dispatch(getCar(router.query.id))
}
}, [])
*/
return (
<Container>
<h2>{car.model}</h2>
</Container>
)
}
CarPage.getInitialProps = async (ctx) => {
const dispatch = useDispatch()
dispatch(getCar(ctx.query.id))
}
To persist redux store through a page reload, we definitely need to use browser storage.
I suggest using https://github.com/rt2zz/redux-persist.
To use dispatch inside getInitialProps, please try with this code snippet instead of using useDispatch() hook.
CarPage.getInitialProps = async ({ store, query }) => {
store.dispatch(getCar(query.id));
return { initialState: store.getState() };
}

correct way to use firestore onSnapShot with react redux

I'm trying to figure out the correct way to use firestore.onSnapshot with react-redux.
I currently have this code in my action file, which I am calling on componentWillMount() in my component.
export const fetchCheckins = () => async (dispatch) => {
const {currentUser} = firebaseService.auth();
try {
let timestamp = (new Date());
//set timestamp for beginning of today
timestamp.setHours(0);
//get checkins today
let checkinstoday = (await firebaseService.firestore().collection(`/checkins/${currentUser.uid}/log`).where("timestamp",">=",timestamp).orderBy("timestamp","desc").get()).docs.map(doc => doc.data());
//set timestamp for beggining of week
timestamp.setDate(-(timestamp.getDay()));
//get checkins (week)
let checkinsweek = (await firebaseService.firestore().collection(`/checkins/${currentUser.uid}/log`).where("timestamp",">=",timestamp).orderBy("timestamp","desc").get()).docs.map(doc => doc.data());
//set timestamp for begging of month
timestamp.setDate(0);
//get checkins (month)
let checkinsmonth = (await firebaseService.firestore().collection(`/checkins/${currentUser.uid}/log`).where("timestamp",">=",timestamp).orderBy("timestamp","desc").get()).docs.map(doc => doc.data());
dispatch({type: FETCH_CHECKINS, payload: { today: checkinstoday, week: checkinsweek, month: checkinsmonth}});
}
catch(e){
console.error(e);
}
};
this works fine, the correct data is sent to the component and display. The problem is, that if the user checks in, the checkin data should adjust, but it does not, since I am getting the data once and sending it, and the state is not re-rendering.
My question is how I should approach this? Do I use .onSnapshot() instead of .get()? Do I call .fetchCheckins() from the .checkin() action creator? How do I approach according to best practice? thank you
According to firestore's documentation if you need realtime updates you should use onSnapshot:
https://firebase.google.com/docs/firestore/query-data/listen
In your case if you use .get() - you get the update once and firestore won't notify you if any of the data changes. That's why you are not seeing the changes.
P.S. checkout redux-firestore: https://github.com/prescottprue/redux-firestore - it's nice library that can help you with your redux bindings.
You could subscribe your list like this:
function subscribeToExperiences() {
return eventChannel((emmiter: any) => {
experiencesRef.onSnapshot({ includeMetadataChanges: true }, snapshot => {
const experiences: IExperience[] = snapshot.docChanges().map(change => ({
id: change.doc.id,
title: change.doc.data().title
}));
if (snapshot.docChanges().length !== 0) {
emmiter(experiences);
}
});
return () => experiencesRef;
});
}
function* fetchExperiences(_: ExperiencesFetchRequested) {
const channel = yield call(subscribeToExperiences);
try {
while (true) {
const experiences = yield take(channel);
yield put(new ExperiencesFetchSucceeded(experiences));
}
} finally {
if (yield cancelled()) {
channel.close();
}
}
}
subscribeToExperiences uses a redux-saga eventChannel. An eventChannel receives an emmiter that generates a saga effect to be consumed with take. The eventChannel has to return a function to close the connections but afaik .onSnapshot connections don't need to be explicitly closed, that's why I return a dummy function.

Resources