Create and add multiple map(objects) fields in document in React Native Firebase Firestore - firebase

I am new to React Native, and I am trying to build an app.
The problem I am facing now, is creating map(objects) in documents of firebase firestore using firebase.firestore().... .add function.
I've managed to get simple string inputs to save(name, title and age - what user types in text input are successfully saved) but I didn't find a way to make it save to objects(named maps in firebase firestore) & also let user add infinite teammates input in frontend.
My goal is for the user to be able to input this:
Age, Name, Title(already solved) and Teammates
So the document in firebase should have (Age:Str, Name:Str, Title:Str, Teammates:NestedObject(map)>Teammate1(Object)>Name,Title
-- I attached pictures of UI, and database currentDoc, and neededDoc for better clarification
How should I go about it? Would love if you could help me. Thanks
Pics: UserUI: https://imgur.com/a/7CsPeQQ
actual Firestore Doc: https://imgur.com/a/0Up1i8S
needed Firestore Doc: https://imgur.com/a/OpzGl4x
EDIT /// SOLVED Firebase Map/Object uploading issue. However I still couldn't find a way to add n inputs to object.
import React, {useState} from 'react';
import { View, Image, TextInput, Button } from 'react-native';
import firebase from 'firebase';
import { user } from '../redux/reducers/user';
require("firebase/firestore") ;
require("firebase/firebase-storage") ;
export default function Save(props, {navigation}) {
const [age, setAge] = useState("");
const [name, setName] = useState("");
const [title, setTitle] = useState("");
// const [teammates, setTeammates] = useState("");
const savePostData = () => {
firebase.firestore()
.collection('posts')
.doc(firebase.auth().currentUser.uid)
.collection("userPosts")
.add({
teammates:{},
age,
name,
title,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
}).then((function () {
props.navigation.popToTop()
}))
}
return (
<View style={{flex:1}}>
<TextInput
placeholder = "Age"
onChangeText = {(age) => setAge(age) }
/>
<TextInput
placeholder = "Name"
onChangeText = {(name) => setName(name) }
/>
<TextInput
placeholder = "Title"
onChangeText = {(title) => setTitle(title) }
/>
<Button title="Post" onPress = {() => savePostData()} />
</View>
)
}

Related

Firebase Facebook Auth - React Native - Mobile App

I am having trouble implementing "Sign in with Facebook" Authentication in my mobile application. I have a feeling I am missing something tiny but can't seem to crack it.
StartScreen.js
import {signInWithFacebook, FacebookAuthProvider, signInWithPopup} from 'firebase/auth'
import { auth, facebookProvider } from '../firebase'
const signInWithFacebook = () => {
signInWithFacebook(auth, facebookProvider)
.then((re) =>{
console.log(re);
})
.catch((err)=>{
(err.message)
})
}
I have tried signUpWithPopUp and signInWithFacebook, but I am beginning to think there is an issue inside the function itself. With the current screenshot, I am receiving a "Maximum call stack size exceeded." error.
Firebase.js
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const facebookProvider = new FacebookAuthProvider();
export { auth, facebookProvider }
I have tried removing facebookProvider from the function, using just "auth", and that is not working either, receiving the same error.
I have attempted different combinations between the parameters I am passing and whether I am using signInWithFaceBook or signInWithPopup.
In case it is relevant, here is the button I am using:
startscreen.js
<TouchableOpacity
onPress = {signInWithFacebook}>
<View
style={[
styles.login_social_button,
styles.login_social_facebook,
]}>
<List.Icon style = {styles.login_social_icon} icon="facebook" />
</View>
</TouchableOpacity>
Thank you

Best way to capture screen time and press events using React Native Expo and Firebase Analytics

My group and I are currently working on a mobile app using expo-cli and firebase as the backend. One of the requirements is we need to get users' screen time and record how frequently users press certain buttons. According to expo firebase documentation, it only supports limited Firebase Analysis. We were wondering what would be the best way to use Firebase Analytics with Expo to capture screen time and button pressed frequencies.
Screen Tracking
Screen tracking in React Native is different than in a native app since some navigation libraries run inside one Activity/Viewcontroller.
Assuming you are using react-native-navigation, which does have full native navigation support you can handle screen tracking like this.
import analytics from '#react-native-firebase/analytics';
import { Navigation } from 'react-native-navigation';
Navigation.events().registerComponentDidAppearListener(async ({ componentName, componentType }) => {
if (componentType === 'Component') {
await analytics().logScreenView({
screen_name: componentName,
screen_class: componentName,
});
}
});
Look here for the documentation
If you are using react-navigation you can still work around the lack of native screen support by hooking into the events that are provided.
import analytics from '#react-native-firebase/analytics';
import { NavigationContainer } from '#react-navigation/native';
const App = () => {
const routeNameRef = React.useRef();
const navigationRef = React.useRef();
return (
<NavigationContainer
ref={navigationRef}
onReady={() => {
routeNameRef.current = navigationRef.current.getCurrentRoute().name;
}}
onStateChange={async () => {
const previousRouteName = routeNameRef.current;
const currentRouteName = navigationRef.current.getCurrentRoute().name;
if (previousRouteName !== currentRouteName) {
await analytics().logScreenView({
screen_name: currentRouteName,
screen_class: currentRouteName,
});
}
routeNameRef.current = currentRouteName;
}}
>
...
</NavigationContainer>
);
};
export default App;
Here you can find a full example starter app.
Button Press Events
For logging press events there's a lot of documentation on the RNFirebase website.
A simple example to track a custom event that could be an onPress or anything would look like this:
import react, { useEffect } from 'react';
import { View, Button } from 'react-native';
import analytics from '#react-native-firebase/analytics';
function App() {
return (
<View>
<Button
title="Add To Basket"
onPress={async () =>
await analytics().logEvent('onPressAddToBasket', {
id: 3745092,
item: 'Your product name',
description: ['round neck', 'long sleeved'],
size: 'L',
wheneverYouWantToTrack: true,
})
}
/>
</View>
);
}

React Native | Firebase Firestore | Storing user data only after pressing twice

What I expect: When I press the text "sign up" once it will both create a user in firebase auth and then create a user in firestore with the single property.
What is actually happening: When I press the text "Sign Up" it immediately creates a user in firebase auth, but then it only creates a user in firestore with the property once I press either the username field, the password field, or the Sign Up button for a second time.
I suspect: That this has to do something with promises and my nested .then's but can't figure out why it is operating this way; seems really strange.
Sample code:
import React from "react";
import * as firebase from "firebase";
import "firebase/firestore";
const Screen = () => {
const firestore = firebase.firestore();
const writeUserData = uid => {
const docRef = firestore.doc(`users/${uid}`);
docRef
.set({
example_property
})
.then(item =>
console.log("successfully added user to the collection" + item)
)
.catch(err => {
console.log(err);
});
};
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.submitButton}
onPress={() =>
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(userObj => writeUserData(userObj.user.uid))
.catch(error => {
var errorCode = error.code;
var errorMessage = error.message;
console.log("errorCode: " + errorCode);
console.log("errorMessage: " + errorMessage);
})
}
>
<Text>Sign Up</Text>
</TouchableOpacity>
</View>
);
};
It now works that I've removed the anonymous function from the .then in writeUserData. See here:
const writeUserData = uid => {
const docRef = firestore.doc(`users/${uid}`);
docRef
.set({
example_property
})
.then(console.log("successfully added user"))
.catch(err => {
console.log(err);
});
};

Redux, Firebase, and react-native: fetch data async

I'm developing a React-native app, using Redux and Firebase.
My Firebase database is denormalized, so it looks like:
users:
user_uid:
my_posts: [ post_key1, post_key2 ]
posts
post_key1: { post_details }
post_key2: { post_details }
How should I fetch data asynchronously and dispatch posts data to Redux store?
I know about Firebase methods .on('value') and .once('value'), but I'm not able to write a proper async function/thunk without generating issues.
If you are using react-redux-firebase to integrate redux with Firebase, the v2.0.0 docs show using react-native with examples for using either native-modules through react-native-firebase or the JS SDK.
With the structure you have shown, it may also be helpful for you to use populate to easily load posts automatically when loading users.
If you have the users uid on the post object under owner, you could do something like:
Home.js
import { compose } from 'redux'
import { connect } from 'react-redux'
import { firebaseConnect, populate } from 'react-redux-firebase'
const populates = [
{ child: 'owner', root: 'users' } // replace owner with user object
]
const enhance = compose(
firebaseConnect([
// passing populates parameter also creates all necessary child queries
{ path: 'posts', populates }
]),
connect(({ firebase }) => ({
// populate original from data within separate paths redux
posts: populate(firebase, 'posts', populates),
}))
)
const SomeComponent = ({ posts }) => <div>{JSON.stringify(posts, null, 2)}</div>
export default enhance(SomeComponent)
App.js
import { createStore, combineReducers, compose } from 'redux'
import { connect } from 'react-redux'
import { reactReduxFirebase, firebaseReducer } from 'react-redux-firebase'
import firebase from 'firebase'
import Home from './Home' // code above
const firebaseConfig = {} // config from firebase console
// react-redux-firebase config
const rrfConfig = {
userProfile: 'users' // automatically manage profile
}
// initialize firebase instance
firebase.initializeApp(config) // <- new to v2.*.*
// Add reduxReduxFirebase enhancer when making store creator
const createStoreWithFirebase = compose(
reactReduxFirebase(firebase, rrfConfig)
)(createStore)
// Add Firebase to reducers
const rootReducer = combineReducers({
firebase: firebaseStateReducer
})
// Create store with reducers and initial state
const initialState = {}
const store = createStoreWithFirebase(rootReducer, initialState)
const App = () => (
<Provider store={store}>
<Home />
</Provider>
);
ReactDOM.render(<App/>, document.querySelector('#app'));

Use async react-select with redux-saga

I try to implement a async react-select (Select.Async). The problem is, we want to do the fetch in redux-saga. So if a user types something, the fetch-action should be triggered. Saga then fetches the record and saved them to the store. This works so far.
Unfortunately loadOptions has to return a promise or the callback should be called. Since the newly retrieved options get propagated with a changing property, I see no way to use Select.Async together with saga to do the async fetch call. Any suggestions?
<Select.Async
multi={false}
value={this.props.value}
onChange={this.onChange}
loadOptions={(searchTerm) => this.props.options.load(searchTerm)}
/>
I had a hack where i assigned the callback to a class variable and resolve it on componentWillReceiveProps. That way ugly and did not work properly so i look for a better solution.
Thanks
redux-saga is for handling side effects like asynchronously receiving options for react-select. That's why you should leave the async stuff to redux-saga. I have never used react-select but by just looking at the documentation I would solve it this way:
Your component gets very simple. Just get value and options from your redux store. optionsRequested is an action creator for the OPTIONS_REQUESTED action:
const ConnectedSelect = ({ value, options, optionsRequested }) => (
<Select
value={value}
options={options}
onInputChange={optionsRequested}
/>
)
export default connect(store => ({
value: selectors.getValue(store),
options: selectors.getOptions(store),
}), {
optionsRequested: actions.optionsRequested,
})(ConnectedSelect)
A saga definition watches for OPTIONS_REQUESTED action that is trigged by onInputChange, loads the data with given searchTerm from server and dispatches OPTIONS_RECEIVED action to update redux store.
function* watchLoadOptions(searchTerm) {
const options = yield call(api.getOptions, searchTerm)
yield put(optionsReceived(options))
}
In other words: Make your Component as pure as possible and handle all side-effect/async calls in redux-saga
I hope this answer was useful for you.
The main idea is that you are capable to dispatch redux actions using application context from
import React from 'react';
import { connect } from 'react-redux';
import Select from '#components/Control/Form/Skin/Default/Select';
import { reduxGetter, reduxSetter, required as req } from '#helpers/form';
import { companyGetTrucksInit } from "#reduxActions/company";
import AppContext from '#app/AppContext';
const FIELD_NAME = 'truck';
export const getReduxValue = reduxGetter(FIELD_NAME);
export const setReduxValue = reduxSetter(FIELD_NAME);
const SelectCompanyTruck = (props) => {
const {
required,
validate=[]
} = props;
const vRules = [...validate];
if (required)
vRules.push(req);
return (
<AppContext.Consumer>
{({ dispatchAction }) => (
<Select
loadOptions={(inputValue, callback) => {
function handleResponse(response) {
const { data: { items } } = response;
const options = items.map(i => ({ label: i.name, value: i.id }));
callback(options);
}
dispatchAction(companyGetTrucksInit, { resolve: handleResponse, inputValue });
}}
name={FIELD_NAME}
{...props}
/>
)}
</AppContext.Consumer>
);
}
export default SelectCompanyTruck;

Resources