User Authorization with Firebase Walkthrough
Useful Links
- Final Github-Repo of Crypto Hunter
- Live-Site
- User Auth and Watchlist Tutorial
- Original Crypto Tracker Tutorial
- Starting Github-Repo
What is Firebase?
Firebase concept is simple. When you build a client-side app with JavaScript or any of its frameworks, for instance, Google Firebase can turn this into a serverless app in no time. It also removes the need to manage databases yourself, as it does that for you.
Firebase is a backend platform for building Web, Android and IOS applications. It offers real time database, different APIs, multiple authentication types and hosting platform.
Adding Firebase
- Go to Firebase
- Login and Create New Project
- Give your project a name
- Accept terms and options SEO
- From within your apps console
- Register your app
- Install firebase via NPM
- npm i firebase
- Copy config file firebase provides
- add firebaseConfig.js inside config folder inside of src and paste our config
- Recommend add .gitignore and configuring .env file for firebaseConfig
- export default firebaseConfig
Choosing our Firebase Build
- In this project we want to choose Authentication
- We want to add Email/Password for Native providers
- Google for Additional Providers
- Confirm Project public-facing-name, typically default is fine.
- Add Project support email and Save
Integrating Firebase to your project
-
Create a new file called firebase.js in your src folder
-
Inside lets create the const firebaseApp and initialize with our firebaseConfig file
- Don't forget to import initialize from firebase/app
-
Create const auth = getAuth(firebaseApp)
-
Import the auth library from 'firebase/auth'
-
Create const db = getFirestore(firebaseApp)
-
Import firestore library from 'firebase/firestore'
-
Export auth and db to be utilized outside of this file.
Completed Example of 'firebase.js'
1import firebaseConfig from './config/firebaseConfig'; 2import { initializeApp } from 'firebase/app'; 3import { getAuth } from 'firebase/auth'; 4import { getFirestore } from 'firebase/firestore'; 5 6const firebaseApp = initializeApp(firebaseConfig); 7 8const auth = getAuth(firebaseApp); 9const db = getFirestore(firebaseApp); 10 11export { auth, db }; 12
Adding to our Context State
- In our Crypto Demo application we utilize Context API so we can tap into our state object without prop drilling.
- Since we want to add this to Login and have our user status accessible throughout the application this is an ideal location to configure
- Add user state to our crypto context.
CryptoContext Example
1const CryptoContext = ({ children }) => { 2 const [currency, setCurrency] = useState('INR'); 3 const [symbol, setSymbol] = useState('₹'); 4 const [coins, setCoins] = useState([]); 5 const [loading, setLoading] = useState(false); 6 const [user, setUser] = useState(null) 7
-
Add Material UI Modal Component for Login Button
- Inside of Components folder lets make Authentication folder
- Lets add AuthModal.js to the Authentication folder
- Copy Modal of Choice from MUI website and paste into AuthModal.js and rename the modal export to AuthModal
- Inside of Header.js Lets add <AuthModal/> after Select but inside of Toolbar. Remember to Import.
- Lets Remove the default button and add our own next in AuthModal.js
-
Add <Button> from Material-UI
- Login will be the content of the button
- variant will be 'contained'
- style get from example below
- Add onClick={handleOpen}
New MUI Button
1<Button 2 variant="contained" 3 style={{ 4 width: 85, 5 height: 40, 6 marginLeft: 15, 7 backgroundColor: '#EEBC1D', 8 }} 9> 10 Login 11</Button> 12
-
Update Fade Component inside our AuthModal
- Remove all default tags inside our <div> inside of <Fade>
- Add MUI TABS component to our Auth Modal
- Import Tabs
- Add <AppBar> inside our div
- Notice that tabs needs 'value' and 'handleChange'
- Lets copy that from our TABS code from MUI documentation
-
Add styles to paper for useStyles inside our modal on AuthModal.js
1 2const useStyles = makeStyles((theme) => ({ 3 modal: { 4 display: 'flex', 5 alignItems: 'center', 6 justifyContent: 'center', 7 }, 8 paper: { 9 width: 400, 10 backgroundColor: theme.palette.background.paper, 11 color: 'white', 12 borderRadius: 10, 13 } 14})); 15
Updating the TAB logic
- Add {value===0 && <Login/>} inside our AuthMOdal After </AppBar>
- Add {value ===1 && <SignUp/>} inside our AuthMOdal After </AppBar>
- Create Login and SignUp Components Inside our Authentication Folder
Configuring Login & Signup
- Import Login and SignUp to AuthModal.js
- We want to use handleClose method inside our Login and SignUp Componenents
- Lets pass handleClose down as a prop
- Add props inside our Login And SignUp Components so we can use them.
Example of HandleClose
1{ 2 value === 0 && <Login handleClose={handleClose} />; 3} 4{ 5 value === 1 && <SignUp handleClose={handleClose} />; 6} 7
-
Inside of our Signup.js
- Configure useState hook for email, password and confirmPassword, set default state to ""
- In our Return we will use the Box Component from MUI core
- Import Box and add the following attributes
- Import TextField and put inside of our Box with the following attributes
- Copy TextField and Paste 2 times and edit for setPassowrd and confirmPassword
- Add a MUI Button next, import and add the Handle Submit onClick
- Check your Modal > SignUP and confirm our UI looks good
MUI MODAL with SIGNUP Code
1return ( 2 <Box p={3} style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}> 3 <TextField 4 variant="outlined" 5 type="email" 6 label="Enter Email" 7 value={email} 8 onChange={(e) => setEmail(e.target.value)} 9 fullWidth 10 ></TextField> 11 <TextField 12 variant="outlined" 13 type="password" 14 label="Enter Password" 15 value={password} 16 onChange={(e) => setPassword(e.target.value)} 17 fullWidth 18 ></TextField> 19 <TextField 20 variant="outlined" 21 type="password" 22 label="Confirm Password" 23 value={confirmPassword} 24 onChange={(e) => setConfirmPassword(e.target.value)} 25 fullWidth 26 ></TextField> 27 <Button 28 variant="contained" 29 size="large" 30 style={{ backgroundColor: '#EEBC1D' }} 31 onClick={handleSubmit} 32 > 33 Sign Up 34 </Button> 35 </Box> 36); 37
-
Setup Our Login.js
-
Copy our return Code and state from SignUp.js
-
Remove useState and TextField with ConfirmPassword References, we won't need it
-
Create handleSubmit const
-
confirm handleClose is a prop in Login
Login.js
1import React, { useState } from 'react'; 2import { Box, Button, TextField } from '@material-ui/core'; 3 4const Login = ({ handleClose }) => { 5 const [email, setEmail] = useState(''); 6 const [password, setPassword] = useState(''); 7 8 const handleSubmit = () => {}; 9 10 return ( 11 <Box 12 p={3} 13 style={{ display: 'flex', flexDirection: 'column', gap: '20px' }} 14 > 15 <TextField 16 variant="outlined" 17 type="email" 18 label="Enter Email" 19 value={email} 20 onChange={(e) => setEmail(e.target.value)} 21 fullWidth 22 ></TextField> 23 <TextField 24 variant="outlined" 25 type="password" 26 label="Enter Password" 27 value={password} 28 onChange={(e) => setPassword(e.target.value)} 29 fullWidth 30 ></TextField> 31 32 <Button 33 variant="contained" 34 size="large" 35 style={{ backgroundColor: '#EEBC1D' }} 36 onClick={handleSubmit} 37 > 38 Sign Up 39 </Button> 40 </Box> 41 ); 42}; 43 44export default Login; 45
HandleSubmit PW Logic on SignUp
-
Inside of HandleSugbmit lets add pw logic if (password !== confirmPassword)
-
Next we want to add a SnackBar alert from MUI to prompt
- Before we can implement we want to create a Context for our Alerting
- Inside CryptoContext alert/setAlert state with {open: false, message: '', type: 'success'}
- Remember to add the value into our provider to be passed down to our children.
Updated Context with Alerting
1const [alert, setAlert] = useState({ 2 open: false, 3 message: '', 4 type: 'success', 5}); 6 7return ( 8 <Crypto.Provider 9 value={{ currency, setCurrency, symbol, coins, loading, alert, setAlert }} 10 > 11 {children} 12 </Crypto.Provider> 13); 14
- New Alert.js Component Setup
- Create the component named Alert.js inside Components folder
- Import alert, setAlert from our CryptoState()
- Inside our return lets add the Snackbar from MUI
- Copy the JS example Snackbar code and paste inside of Alert.js
- Just copy the logic, no styles or return code needed
- Remove open/setOpen state, it already inside our alert state.
- Remove default handleClick also
- Change setOpen(false) to setAlert({open: false}) to match our logic in cryptoState
- copy props from example code too, open, autoHideDuration, onClose
- Import MuiAlert and put inside our snackbar
- Add props (see example below)
- Finally add {alert.message} as the content for our MuiAlert Component
Completed Alert.js Example
1import { Snackbar } from '@material-ui/core'; 2import React from 'react'; 3import { CryptoState } from '../CryptoContext'; 4import MuiAlert from '@material-ui/lab/Alert' 5 6const Alert = () => { 7 const { alert, setAlert } = CryptoState(); 8 9 10 const handleClose = (event, reason) => { 11 if (reason === 'clickaway') { 12 return; 13 } 14 15 setAlert({open: false}); 16 return ( 17 <Snackbar 18 open={alert.open} 19 autoHideDuration={3000} 20 onClose={handleClose} 21 > 22 <MuiAlert 23 onClose={handleClose} 24 elevation={10} 25 variant="filled" 26 severity={alert.type} 27 > 28 {alert.message} 29 </MuiAlert> 30 </Snackbar> 31 ) 32}; 33 34export default Alert; 35 36
Adding our Alerts to SignUp.js
- import {CryptoState} from '../../CryptoContext.js'
- Lets destructure setAlert from CryptoState();
- Now we can add setAlert inside our password if block
- we will pass the type of alert we want when our PWs don't match
- Followed by an empty return
- Also we need to import our Alert into our APP.js
- Test your PW logic now on SignUp and see if your Alert pops
SignUp.js HandleSubmit
1const handleSubmit = () => { 2 if (password !== confirmPassword) { 3 setAlert({ 4 open: true, 5 message: 'Passwords do not match', 6 type: 'error', 7 }); 8 return; 9 } 10}; 11
Adding Try Catch Block
-
After our if/password check we want to add a try/catch but still iside our handleSubmit function
-
Insided of our try {} lets add
- const result = await createuserWithEmailAndPassword()
- Add async to our handleSubmit before our argument ()
- Pass auth as the first arg and make sure to import
- Pass email and password as the last two arguments
- setAlert with a confirmation and welcome of the user with their email
- Pass handleClose() so it will timeout our alert last
- add error to your catch block arg
- setAlert inside our catch and pass the error message (see example below)
- Test SignUp and you should get Alerting
- Also lets console.log(result) at the bottom of our try block
- You can now see the information that result has access too, and how we are able to provide that information to the message of our Alert
Try/Catch Error Handling with Firebase
1import { auth } from '../../firebase'; 2 3try { 4 const result = await createUserWithEmailAndPassword(auth); 5 setAlert({ 6 open: true, 7 message: `Sign Up Successful. Welcome ${result.user.email}`, 8 type: 'success', 9 }); 10 handleClose(); 11} catch (error) { 12 setAlert({ 13 open: true, 14 message: error.message, 15 type: 'error', 16 }); 17 return; 18} 19
Configuring our Login.js HandleSubmit
- Destructure setAlert from our CryptoState()
- add if no email/password logic and set alert with return
- Now we can copy our SignUp catch logic as that will be the same
- we can create const result = await signInWithEmailAndPassword
- make sure to add async to the handleSubmit
- pass auth, email, password remember to import auth from firebase
- add console.log(result)to see our result object data on login
- Test with Login
Is User Logged In?
-
We need to be able to tell if our user is now logged in or not or it will get confusing for your user experience
-
Lets open CryptoContext again as we will need to be able to tap into this data throughout our App
-
First lets import and add another useEffect
-
lets call onAuthStateChanged(auth), this is a firebase method and pass auth and then a callback
-
that callback will be passed user,
-
if user setUser to user else set to null
useEffect
1useEffect(() => { 2 onAuthStateChanged(auth, (user) => { 3 if (user) setUser(user); 4 else setUser(null); 5 }); 6}, []); 7
Creating our User in Header
- Add user to our CryptoState destructure
- Now we have the user Object lets Wrap our AuthModal in jsx logic
1{user ? 'Logout' : <AuthModal />}
- Now we can test to confirm Logout display on login
Removing Logout String and Adding UserSideBar
- Replace 'logout' with <UserSideBar>
- Create <UserSideBar> Component inside of our Authentication folder
- We should see the sidebar text inside our our button for now.
- Lets grab the Drawer code from MUIv4
- Copy the Source and add to our UserSideBar
- Now we need to remove the unneeded default code
- Remove all the list references
- Add hello inside of our Drawer for now
- Remove all options that aren't 'right'
- Delete all imports that we no longer need, should be yellow lines underneath your imports in your IDE
- it should show right on your button now and a small side bar
Configuring our SideBar
- Lets first replace the Button with an Avatar in our UserSideBar.js
- We will need the user from the context to get the users avatar photo
- we will import this Component form MUIv4
- Now lets replace <Button> with <Avatar> and pass the onClick with toggleDrawer and the default args, anchor & true
- Add src prop to avatar and pass our user object with the photoURL (See Example Below)
- Add alt prop and pass the userobject with displayName OR email
Example Avatar
1 <Avatar 2 onClick={toggleDrawer(anchor, true)} 3 style={{ 4 height: 38, 5 width: 38, 6 marginLeft: 15, 7 cursor: 'pointer', 8 backgroundColor: '#EEBC1D', 9 }} 10 src={user.photoURL} 11 alt={user.displayName || user.email} 12 /> 13 <Drawer 14 anchor={anchor} 15 open={state[anchor]} 16 onClose={toggleDrawer(anchor, false)} 17 > 18 Hello 19 </Drawer> 20
- Styling the UserSideBar and Avatar
- Remove all the styles we copied inside our UserSideBar.js useStyles object
- Copy Container Snippet (see below for code)
- Lets add the profile style for the profile class (see below for code)
- Lets add the picture style for the picture class (see below for code)
- Lets add the logout style for the logout button class (see below for code)
- Lets add the watchlist style for the watchlist class (see below for code)
- Remove all the styles we copied inside our UserSideBar.js useStyles object
Styling Example Code
1const useStyles = makeStyles({ 2 container: { 3 width: 350, 4 padding: 25, 5 height: '100%', 6 display: 'flex', 7 flexDirection: 'column', 8 fontFamily: 'monospace', 9 }, 10 profile: { 11 flex: 1, 12 display: 'flex', 13 flexDirection: 'column', 14 alignItems: 'center', 15 gap: '20px', 16 height: '92%', 17 }, 18 picture: { 19 width: 200, 20 height: 200, 21 cursor: 'pointer', 22 backgroundColor: '#EEBC1D', 23 objectFit: 'contain', 24 }, 25 logout: { 26 height: '8%', 27 width: '100%', 28 backgroundColor: '#EEBC1D', 29 marginTop: 20, 30 }, 31 watchlist: { 32 flex: 1, 33 width: '100%', 34 backgroundColor: 'grey', 35 borderRadius: 10, 36 padding: 15, 37 paddingTop: 10, 38 display: 'flex', 39 flexDirection: 'column', 40 alignItems: 'center', 41 gap: 12, 42 overflowY: 'scroll', 43 }, 44}); 45
- Lets add classes.container to the first <div>inside of <Drawer>
- Lets add classes.profile to the second <div> inside of <Drawer>
- Copy our Avatar Tag and Paste inside our 2nd <div>.
- Remove the styles object and onClick
- After our 2nd Avatar tag, lets add a span (see example below for styles) that displays user.displayName or user.email
UserSideBar with Span
1<React.Fragment key={anchor}> 2 <Avatar 3 className={classes.picture} 4 onClick={toggleDrawer(anchor, true)} 5 style={{ 6 height: 38, 7 width: 38, 8 cursor: 'pointer', 9 backgroundColor: '#EEBC1D', 10 }} 11 src={user.photoURL} 12 alt={user.displayName || user.email} 13 /> 14 <Drawer 15 anchor={anchor} 16 open={state[anchor]} 17 onClose={toggleDrawer(anchor, false)} 18 > 19 <div className={classes.container}> 20 <div className={classes.profile}> 21 <Avatar 22 className={classes.picture} 23 src={user.photoURL} 24 alt={user.displayName || user.email} 25 /> 26 <span 27 style={{ 28 width: '100%', 29 fontSize: 25, 30 textAlign: 'center', 31 fontWeight: 'bolder', 32 wordWrap: 'break-word', 33 }} 34 > 35 {user.displayName || user.email} 36 </span> 37 </div> 38 </div> 39 </Drawer> 40</React.Fragment> 41
Adding our Logout Button
- Lets add Button after our 1st <div> after <span> on our UserSideBar.js
- Next we need to create our logOut Function
- inside logout lets add the firebase function signOut()
- this should auto import the signout method from firebase/auth
- Lets import {auth} from '../../firebase' and pass it into the signOut fucntion
- Add setAlert to our destructured objects from CryptoState so we can use it during logout
- Now we can add the setAlert fucntion and pass the example code below.
- Don't forget to toggleDrawer() so our drawer closes upon logout.
LogOut Button and LogOut Function for UserSideBar.js
1const logOut = () => { 2 signOut(auth); 3 setAlert({ 4 open: true, 5 type: 'success', 6 message: 'Logout Successfull!', 7 }); 8 toggleDrawer(); 9}; 10 11<Button variant="contained" className={classes.logout} onClick={logOut}> 12 Log Out 13</Button>; 14
Adding Google Authentication
- Inside of our AuthModal.js inside authentication folder
- Lets add a Box with the className of classes.google and import from MUI
- Add a span inside that box with OR
- Following our <span> lets add <GoogleButton>
- Import GoogleButton from 'react-google-button'
- lets create an empty function after our handleChange named signInWithGoogle
- Test and going to login prompt, confirm our Sign In with google Button Exists
- Now lets add the google styles to our AuthModal.js
1 google: { 2 padding: 24, 3 paddingTop: 0, 4 display: 'flex', 5 flexDirection: 'column', 6 textAlign: 'center', 7 gap: 20, 8 fontSize: 20, 9 }, 10
-
Adding Google Auth Provider
-
const googleProvider = new GoogleAuthProvider()
- import googleAuthProvder from 'firebase/auth'
-
create const signInWithPopup (this is a firebase function)
- import signInWithPopup from 'firebase/auth'
- pass auth as the first arg, don't forget to import
- pass googleProvider as our 2nd arg
-
add .then()
- const setAlert from CryptoState()
- import CrpytoState
- Add handleClose() to shut the login prompt
- const setAlert from CryptoState()
-
add .catch() for our error handling
- pass setAlert(see example for props)
- return
-
Test the Google Auth Login now, it should work!
-
Google Provider Code
1 2 const { setAlert } = CryptoState(); 3 4 const googleProvider = new GoogleAuthProvider(); 5 6 const signInWithGoogle = () => { 7 signInWithPopup(auth, googleProvider) 8 .then((res) => { 9 setAlert({ 10 open: true, 11 message: `Sign Up Successful. Welcome ${res.user.email}`, 12 type: 'success', 13 }); 14 handleClose(); 15 }) 16 .catch((error) => { 17 setAlert({ 18 open: true, 19 message: error.message, 20 type: 'error', 21 }); 22 return; 23 }); 24
Adding FIRESTORE for our WATCHLIST storage
- Go to Firebase Dashboard, under BUILD find FIRESTORE DATABASE. Click
- Swith radio start in test
- Test mode allows you more than 30 days of development for free
- Select a Server, default works
- Click Rule tab
- We want only logged in users to be able to add coins to their watchlist
- first remove the line that turns off the DB after 30 days.
- Next lets remove the code inside the first match block
Adding Endpoints to your Firstore DB
- add api endpoints you want to be asssociated with this data
- match /watchlist/{userId}
- We can create a function for our read and pass our userId to create a cleaner code
FireStore Endpoint Logic for Watchlist
1rules_version = '2'; 2service cloud.firestore { 3 match /databases/{database}/documents { 4 match /watchlist/{userId}{ 5 allow read: if isLoggedIn(userId) 6 allow write: if request.auth.uid == userId 7 } 8 } 9 function isLoggedIn(userId){ 10 return request.auth.uid == userId 11 } 12} 13
Add to Watchlist Button and Frontend Logic
- First open CoinPage.js inside Pages
- Lets add user to our CryptoState Destructure
- We only want to display the button IF the user is loggedIn
- Go underneath our <span> tag and add {}
Add to Watchlist Button
1{ 2 user && ( 3 <Button 4 variant="outlined" 5 style={{ width: '100%', height: 40, backgroundColor: '#EEBC1D' }} 6 > 7 Add to Watchlist 8 </Button> 9 ); 10} 11
Adding Add to Watchlist Firebase Logic
- Inside our CoinPage.js lets create a const addToWatchList
- Inside addToWatchList we create another const coinRef
- add const coinRef = doc(db, 'watchlist', user.uid);
- doc is imported form our firestore
- db is imported from firebase
- Add a try catch block next
- add setDoc and import from firestore
- Pass our coinRef as the first arg that has all the doc data
- Pass an object with coins and ternirary logic for if a watchlist currently exists or needs to be created (see example below)
- Add watchlist and setWatchList to our cryptoContext useState
- pass it down via the Provider too
- now we can add watchlist to our destructure on coinpage
- after our setSoc lets add a setAlert to notify our user
- Lets add a variable inWatchList that checks if the coin are viewing is currently on the watchlist.
- Now we can Test and see if our firebase DB has the coin we added!
FireBase Watchlist Endpoint Code
1const addToWatchlist = async () => { 2 const coinRef = doc(db, 'watchlist', user.uid); 3 4 try { 5 await setDoc(coinRef, { 6 coins: watchlist ? [...watchlist, coin.id] : [coin?.id], 7 }); 8 setAlert({ 9 open: true, 10 message: `${coin.name} Added to the Watchlist !`, 11 type: 'success', 12 }); 13 } catch (error) { 14 setAlert({ 15 open: true, 16 message: error.message, 17 type: 'error', 18 }); 19 } 20}; 21
Watchlist Logic on CoinPage.js
1{ 2 user && ( 3 <Button 4 variant="outlined" 5 style={{ width: '100%', height: 40, backgroundColor: '#EEBC1D' }} 6 onClick={addToWatchlist} 7 > 8 {inWatchList ? 'Remove from Watchlist' : 'Add to Watchlist'} 9 </Button> 10 ); 11} 12
Adding Watchlist Logic for If User Is Logged In
-
On CryptoContext we want to create a useEffect
-
if (user){} // If user is logged in
-
add user to useEffect depedency array
-
add our const coinRef = doc() //import doc
- pass db //import too
- pass endpoint // watchlist
- pass user id // user.uid
-
Now we can add onSnapshot() and check the db for changes to our reference
- Add coinRef as first arg
- (coin) as our second arg
- add if coin.exists() setWatchlist
- pass coin.data().coins
- else log "no items in Watchlist"
-
lets add var unsubscribe to our onSnapshot
-
Last add a return anonymous function and call unsubscribe()
-
Watchlist Example
1useEffect(() => { 2 if (user) { 3 const coinRef = doc(db, 'watchlist', user.uid); 4 5 var unsubscribe = onSnapshot(coinRef, (coin) => { 6 if (coin.exists()) { 7 setWatchlist(coin.data().coins); 8 } else { 9 console.Console('No Items in Watchlist'); 10 } 11 }); 12 return () => { 13 unsubscribe(); 14 }; 15 } 16}, [user]); 17
Remove from Watchlist
-
Now we need to implement removeFromWatchlist
- On our CoinPage lets add const removeFromWatchList function
- copy our logic from addToWatchList as it will be similiar
-
Updating setDoc in our Try Block
- add coins: watchlist.filter((watch) => watch !== coin?.id)
- this takes our watchlist and updates the watchlist to have all coins but the coin we reference
- Add {merge: 'true'} to update hte doc with the new watchlist filtered data
- Update setALert to say "removed from watchlist instead of added"
Updated Remove From Watchlist Code
1const removeFromWatchList = async () => { 2 const coinRef = doc(db, 'watchlist', user.uid); 3 4 try { 5 await setDoc( 6 coinRef, 7 { 8 coins: watchlist.filter((watch) => watch !== coin?.id), 9 }, 10 { merge: true } 11 ); 12 setAlert({ 13 open: true, 14 message: `${coin.name} Removed the Watchlist !`, 15 type: 'success', 16 }); 17 } catch (error) { 18 setAlert({ 19 open: true, 20 mesage: error.message, 21 type: 'error', 22 }); 23 } 24}; 25
Displaying our Watchlist items in sidebar
- Go to UserSideBar.js
- Lets add our watchlist from CryptoState so we can tap into our watchlist coins
- under our Watchlist span
- add {coins.map(coin => {})}
- add if watchlist includes (coin.id)
- return a <div> with classes.coin
- inside that div, put a span with {coin.name}
- add another span after with style (see example code)
- inside that span put {symbol} from our Context API and import
- After symbol Paste our numberWithCommas function we cretaed earlier and pass coin.current_price.toFixed(2) and import.
- We need an ICON now to Delete a Coin from the Watchlist
- npm i react-icons --force
- import {AiFillDelete} from 'react-icons/ai'
- Add coin styles (see github) to UserSideBar
Remove Watchlist Coin from UserSideBar Code
1<div className={classes.watchlist}> 2 <span style={{ fontSize: 15, textShadow: '0 0 5px black' }}>Watchlist</span> 3 {coins.map((coin) => { 4 if (watchlist.includes(coin.id)) 5 return ( 6 <div className={classes.coin}> 7 <span>{coin.name}</span> 8 <span style={{ display: 'flex', gap: 8 }}> 9 {symbol} {numberWithCommas(coin.current_price.toFixed(2))} 10 <AiFillDelete 11 style={{ cursor: 'pointer' }} 12 fontSize="16" 13 onClick={() => removeFromWatchlist(coin)} 14 /> 15 </span> 16 </div> 17 ); 18 else return <></>; 19 })} 20</div> 21
Remove from watchlist
- Lets Copy our RemoveFromWatchlist code from CoinPage.js
- We want to add coin as the arg1 for our pasted function
- import doc, db, and setdoc
- add removeFromWatchlist function to onClick for our AiFillDelete and pass coin as the arg
Cons of Google Firebase
- If not properly managed, the cost of maintaining Firebase on a pay-as-you-go service accumulates as reads and writes increase. So maintenance costs can spike at some points.
- It's hard to export data stored in Firestore into another database. Even if you eventually find a way, it often requires a high level of technicality. Plus, it can be quite costly, too.
- It can be less platform-agnostic, as it delves more towards Android than iOS.
- The larger the query result gets, the messier and slower things become.
Noteables
- With Firebase, it's pretty simple to connect and use built-in third-party authentication providers, including Google, Facebook, Twitter, among others.
Learn more about it here