Reading and displaying data from a Firestore Document in React Native - firebase

Background
I'm trying to build an app which shows a number of stores, in the home screen which is a function component (mind this as I need to use hooks) I have a scroll view which shows different stores.
What I need
When the user presses on one of the stores it should redirect it to a screen which has the information of that specific store. I have built the "store detail" screen but with static info, I want to replace all of that information with data stored in a firestore collection.
Question
How would one go about retrieving data from a Firestore collection in react native, then assigning the data from each document to a separate Touchable Opacity (I know about passing params with react navigation, I just don't know which param to pass when working with Firestore), and then displaying that data in the store detail screen?
Sample code for context
App.js
<NavigationContainer>
<Stack.Navigator initialRouteName={user ? 'Home' : 'Login'}
screenOptions={{cardStyle: { backgroundColor: '#FFFFFF' }}}>
<Stack.Screen name="Home"options={{headerShown: false}}>
{props => <HomeScreen {...props} extraData={user} />}
</Stack.Screen>
<Stack.Screen name="Login" component={LoginScreen} options={{headerShown: false}}/>
<Stack.Screen name="Registration" component={RegistrationScreen} options={{headerShown: false}}/>
<Stack.Screen name="storeDetail" options={{title: ''}}>
{props => <storeDetail {...props} extraData={} />}
</Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
In this file you'll see that I've already called some data (Login and Register pass userData to the Home Screen), however in order to implement that method I depended on the response from the authentication method I was using. I imagine although, I will probably need to pass something as extraData, I understand what I should do, I just don't know how to fill the blank spaces.
Thanks a lot in advance!

First, install the Firebase SDK in your app, so you can make queries to your backend.
I don't know if your sample App.js represents the current state of progress on your app, but I'm going to assume that:
you already have your storeDetail screen built
you know the store's id before navigating to the screen (eg in the HomeScreen)
you pass the storeId as a navigation param when navigating to storeDetail
So in storeSetails screen, you can query Firestore when receiving storeId, and save the result to a state variable on success:
const StoreDetailsScreen = ({ route }) => { // route is passed as a prop by React Navigation
const { storeId } = route.params
const [store, setStore] = useState()
const [loading, setLoading] = useState(true) // show a loading spinner instead of store data until it's available
useEffect(() => {
const fetchQuery = async () => {
const storeData = await firestore()
.collection('stores')
.doc(storeId)
.get() // this queries the database
if (storeData) {
setStore(storeData) // save the data to store state
setLoading(false) // set loading to false
} else {
// something went wrong, show an error message or something
}
}
fetchQuery()
}, [storeId])
if (loading) {
return (
<ActivityIndicator/>
)
}
return (
// ... store details
)
}
Then you can use the data in store to render stuff in your screen
<Text>{store.name}</Text>
<Text>{store.email}</Text>
// ...
More info about how to use Firestore in RN: https://rnfirebase.io/firestore/usage

Related

nextjs localStorage getItem

after searching half a day I still not able to getItem from local storage.
the idea is to save some data to local storage and based on that I want to route a user in the Layout component. I am able to save to local storage and delete but not able to get data from it. I get error 'local storage not defined' or 'destroy is not a function'
I have 3 components save, delete and get. save and delete I execute after a client side api call, the get function I need to be working in the Layout as it is the top level for all routes.
I Need a bit help to the right direction please.
---Upadte
I found something that works
export const IsAuth = ()=>{
const [auth, setAuth] = useState();
useEffect(()=>{
if(typeof windows === undefined) return;
const item = localStorage.getItem('ltu');
setAuth(!!item);
},[]);
return auth;
}
now my problem is I have not much understanding of nextjs. I used the Layout to create a theme template, I basically have only 3 pages that can be visited if not logged in and the rest one needs to be logged in. I get so many examples but it seems like I need to verify auth on every single page instead of being able to do this on root/layout level.
all examples I get are without the use of Layout and I am totally stuck.
I want a simple login system just with jwt and check if thats there to show pages.
I could not get the localStorage.getItem() to work in the layout template.
My solution while maybe not perfect is.
in the _app.js I create useState() and pass those along to the menu trough the Layout, in in the menu useEffect() with 'use client' in the useEffect I set the state I need global.
_app.js
export default function App({ Component, pageProps }){
const [isAuth, setAuth] = useState()
const [user, setUser] = useState()
return (
<Layout setAuth={setAuth} isAuth={isAuth} user={user} setUser={setUser}>
<Component user={user} setUser={setUser} isAuth={isAuth} {...pageProps} />
</Layout>
)
}
Layout.js
export default function Layout({ children, setAuth, isAuth, user, setUser }) {
return (
<>
<Headd />
<SideMenu setAuth={setAuth} isAuth={isAuth} user={user} setUser={setUser}/>
<main>
<div className="menu-spacer"></div>
<content>
{children}
</content>
</main>
</>
)
}
menu.js
'use client';
const SideMenu = ({setAuth, isAuth, user, setUser}) => {
useEffect(()=>{
if(typeof windows === undefined) return;
const item = localStorage.getItem('ltu');
setAuth(!!item);
if(item) setUser(JSON.parse(localStorage.getItem('Ud')))
}, [router, router.isReady])
}
Now I can use the {isAuth, user,} on any page and component.
I am pretty sure this is not the right solution, but I could not find any other working solution and no one here yet posted a answer.

Can't exclude debug traffic from Realtime reports Google Analytics 4

The problem:
I want to be able to debug GA4 without polluting my data. I can't seem to do that without removing my internal traffic filter, and then my presence on the app gets reported in Realtime reporting (and only Realtime reporting so far, but i suspect after 48 hours it will show everywhere in Reports).
First, is this expected behavior? Is Realtime reporting 'special'? Does it display dev traffic while other reports don't? If so, ignore the rest.
What I've tried:
I used google chrome with the GA debug extension from here: ga debugger ext to review my website (on localhost).
When my Internal Traffic filter is deactivated and my Developer Traffic filter is activated, Realtime reports show my presence on my app (boo). But I do see data in the debug view (yay).
When both are activated no data is shown in the Realtime reports area (yay), but also no data is shown in the debug view (boo).
Some Code:
import ReactGA from "react-ga4";
function App() {
useEffect(() => {
ReactGA.initialize("G-blablabla");
ReactGA.send("pageview");
},
[]
)
let location = useLocation();
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: location });
}, [location]);
const GenerateAppRoutes = () => {
return (
<>
<Route path="/" element={
<Landing/>
}
/>
<Route path="/blog/design" element={
<BlogPillar
imgSrc={sampleImg}
topicTitle='Design'
articles={
[
{'link': '/blog/design/materials', 'title': 'Materials'},
{'link': '/blog/design/fasteners', 'title': 'Screws'},
]
}
imgAlt=""
/>
}/>
</>
)
}
const AppRoutes = GenerateAppRoutes();
const appRouteObjects = createRoutesFromChildren(AppRoutes);
const GeneratedRoutes = useRoutes(appRouteObjects);
return (
<>
<NavBarComponent/>
{GeneratedRoutes}
</>
)
}
export default App;

Storyblok React Bridge Restarting Preview On Data Change

I want to use the new Storyblok React bridge with Next.js. In my case, there is a component directly in the _app.js (which should not change between route changes), so it looks like this:
function MyApp({ Component, pageProps: { pageData, globalData } }) {
const story = useStoryblokState(globalData.story)
console.log('rendering app')
return (
<>
<Collage story={story} />
{/* <Component {...pageData} /> */}
</>
);
}
The corresponding getStaticProps looks like this:
export async function getStaticProps() {
let sbParams = {
version: "draft", // or 'published'
};
const storyblokApi = getStoryblokApi();
let { data: pageData } = await storyblokApi.get(`cdn/stories/home`, sbParams);
let { data: globalData } = await storyblokApi.get(
`cdn/stories/collage`,
sbParams
);
return {
props: {
pageData,
globalData,
},
revalidate: 3600,
};
}
Here everything works as intended (i.e. changes to data will only rerender the collage component but not touch anything else), but as soon as I uncomment the main component, in the Storyblok preview (with localhost:3000), changing any data will restart the iFrame, just like it would be the case if no React bridge is used. I can even see that the data changes are applied just before it restarts. Any idea how to fix this?
useStoryblokState() calls useStoryblokBridge() from the #storyblok/js package. Despite its name, useStoryblokBridge() is not a react hook, but an ordinary function with side effects. It subscribes to events from the Storyblok bridge here. If the story that was passed as argument isn't the same story that is sent to the event listener via the Storyblok bridge, the event listener will reload the entire page.
https://github.com/storyblok/storyblok-js/blob/main/lib/index.ts#L40
This means that if you call useStoryblokBridge() with two different stories, the window will reload.

How To Render Data From Firestore To React Native Component?

I have been trying to render information from my firebase to a react native component. I started by console logging what I have done, the data is being fetched completely fine:
displayAllPlayers(){
dbh.collection('Players').get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc.data().First, doc.data().Last)
})
})
}
I then tried to add this information to my component as follows:
displayAllPlayers(){
dbh.collection('Players').get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
<Player key={doc.data().First} fName={doc.data().First} lName={doc.data().Last} />
})
})
}
render() {
const myPlayers = this.displayAllPlayers()
}
return(
{myPlayers}
)
However, I am unable to see anything on my screen. Any clues why?
thanks
React works a bit differently. You need to save the async call response into the state, and then in the render loop check if the state is there, you loop trough it and print the players.
Here is a similar question that has already answered.
I quote the main points that might be useful for you:
Save the data to state when data loaded
render() {
const myPlayers = this.displayAllPlayers()
let data = doc.data();
this.setState({ data: myPlayers });
}
And then render them as you want:
render() {
let dataUI = this.state.data ? <h1>No Data</h1> : <pre>{JSON.stringify(this.state.data)}</pre>;
return (
<div className="classname">
<h1>... </h1>
<div>
<h1>UI Data</h1>
{dataUI}
</div>
</div>
);
}
Also this is a tutorial about Getting started with Cloud Firestore on React Native. Here is another tutorial a little more explained: Create React Native Firebase CRUD App with Firestore.
I encourage you to check both links to have a better understanding.

updating router's history location by dispatching redux action (using react-router-dom)

I have a router that looks something like this
<Router>
<Switch>
<Route path="abc.do" render={() => <SetupAppInitialLoad pageKey="abc" />} />
<Route path="xyz.do" render={() => <SetupAppInitialLoad pageKey="xyz" />} />
</Switch>
</Router>
I have many Route like these inside <Switch>. I am using redux to handle state and dispatching async actions from some components when user clicks on a button/link.
export const asyncExampleAction = () => {
return (dispatch, getState) => {
//logic to send inputs to server comes here
.then(checkStatus)
.then(parseJSON)
.then( (data) => {
if (success case) {
history.push(`something.do?phase=something`);
} else {
//handle error logic
}
})
.catch( (error) => {
console.log(error);
});
}
}
Now, the problem I am facing is, even though history.push updates the url and navigates me to 'something.do?phase=something', but I don't see Route's history.location getting updated. Because of this (I think), when I click on some link on the page 'something.do?phase=something', only url of the page changes, but navigation to that new page doesn't happen.
I checked Programmatically navigate using react router V4 and Programmatically navigate using react router, but still not getting anywhere. Please help.
That happens because react-router does not directly depend on window.history, but instead uses this history project, which wraps window.history and provides react-router with the possibility to listen to location updates and react on them.
You have to options to update navigate from your actions:
Use react-router-redux library.
Or pass history prop from your Component to your actions and use it to navigate inside of them.
Second option is the one officialy recommended by react-router doc:
Rather than dispatching actions to navigate you can pass the history object provided to route components to your actions and navigate with it there.

Resources