Share
Explore

REACT Lab with a Firebase Datastore

Find Working Code:

Documentation:
Adding Firebase to your App

Learning Outcomes:
Create a complete React application with Firebase support
Setting up of the environment
Creating the application
Integrating Firebase.
Running and testing the complete app, executing these instructions in your local development environment.

Step 1: Setting Up The Environment

You'll need Node.js and npm installed on your machine. You can download them from .

Step 2: Create a React App

Use create-react-app to bootstrap a new React project.
npx create-react-app my-firebase-app
cd my-firebase-app

Step 3: Install Firebase

Install Firebase via npm to make it available in your React app.
npm install firebase

Step 4: Create a Firebase Project

Go to the .
Click on "Add project" to create a new Firebase project.
Follow the instructions to set up the project.
Go to the Project settings to find your configuration text settings.

Step 5: Initialize Firebase in Your App

Create a file named firebase.js in your src folder to keep the Firebase configuration.
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';

const firebaseConfig = {
apiKey: "Your API Key",
authDomain: "Your Auth Domain",
projectId: "Your Project ID",
storageBucket: "Your Storage Bucket",
messagingSenderId: "Your Messaging Sender ID",
appId: "Your App ID"
};

firebase.initializeApp(firebaseConfig);

export default firebase;
Fill firebaseConfig with your project's configuration.

Step 6: Authentication

Let's add a simple email and password authentication.

src/AuthService.js

import firebase from './firebase';

export const signUp = (email, password) => {
return firebase.auth().createUserWithEmailAndPassword(email, password);
};

export const signIn = (email, password) => {
return firebase.auth().signInWithEmailAndPassword(email, password);
};

export const signOut = () => {
return firebase.auth().signOut();
};

Step 7: Create Components

Create some components for your app. For example, SignUp.js, SignIn.js, and Home.js.

src/SignUp.js

import React, { useState } from 'react';
import { signUp } from './AuthService';

function SignUp() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');

const handleSubmit = async (e) => {
e.preventDefault();
try {
await signUp(email, password);
alert('User created successfully!');
} catch (error) {
alert(error.message);
}
};

return (
<div>
<h1>Sign Up</h1>
<form onSubmit={handleSubmit}>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" />
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" />
<button type="submit">Sign Up</button>
</form>
</div>
);
}

export default SignUp;
Repeat the similar process for SignIn.js and a homepage Home.js. The SignIn.js will be similar to SignUp.js, but for logging in existing users.

Below is an example of what the SignIn.js component could look like.

This component will handle user sign-in using their email and password.

import React, { useState } from 'react';
import { signIn } from './AuthService';
import { useHistory } from 'react-router-dom';

function SignIn() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const history = useHistory();

const handleSubmit = async (e) => {
e.preventDefault();
try {
await signIn(email, password);
// Redirect to the home page or dashboard after successful sign-in
history.push('/');
} catch (error) {
// Handle sign-in errors here, such as incorrect credentials
alert(error.message);
}
};

return (
<div>
<h1>Sign In</h1>
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit">Sign In</button>
</form>
</div>
);
}

export default SignIn;
In this component:
We use the useState hook to manage the email and password state.
We use useHistory from react-router-dom to navigate to the home page upon successful sign-in.
The signIn function is imported from AuthService.js, which you would define to use Firebase's sign-in methods.
Upon form submission, the handleSubmit function attempts to sign in the user with the provided credentials. If successful, the user is redirected to the home page. If there is an error (e.g., incorrect credentials), it is displayed using an alert (consider using a more user-friendly error handling in a real application).
Please replace the placeholders in the AuthService.js with the Firebase authentication logic, and ensure your project is properly configured to redirect users after sign-in. Also, remember to handle state and error messages appropriately in a real-world application to improve user experience.

The Home.js component would typically be the main landing page after a user successfully logs in. For the purpose of simplicity, let's create a Home.js component that greets the user and provides a sign-out option.

Here is a basic example for Home.js:
import React, { useEffect, useState } from 'react';
import firebase from './firebase';
import { signOut } from './AuthService';
import { useHistory } from 'react-router-dom';

function Home() {
const [currentUser, setCurrentUser] = useState(null);
const history = useHistory();

useEffect(() => {
// Subscribe to auth state changes
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
// User is signed in
setCurrentUser(user);
} else {
// No user is signed in, redirect to sign-in page
history.push('/signin');
}
});

// Cleanup subscription on unmount
return () => unsubscribe();
}, [history]);

const handleSignOut = async () => {
try {
await signOut();
history.push('/signin');
} catch (error) {
// Handle errors here - maybe show a notification or message
console.error('Sign out failed', error);
alert('Sign out failed: ' + error.message);
}
};

return (
<div>
<h1>Home</h1>
{currentUser && <p>Welcome, {currentUser.email}!</p>}
<button onClick={handleSignOut}>Sign Out</button>
</div>
);
}

export default Home;
In this Home.js component:
We use the useState and useEffect hooks to manage the current user's state and respond to changes in the authentication state.
We use the onAuthStateChanged method from Firebase to listen for changes in the user's authentication state.
If there is no authenticated user, we redirect to the sign-in page.
If the user is signed in, we display a greeting and a sign-out button.
The signOut function (not shown here) must be implemented in AuthService.js to handle the Firebase sign-out process.
Here's a simple implementation of the signOut function for AuthService.js:
import firebase from './firebase';

export const signOut = () => {
return firebase.auth().signOut();
};
Ensure that the ./firebase import points to the module where you have initialized your Firebase app and its auth module.

Step 8: Routing

Install React Router for navigation.
npm install react-router-dom
Configure your routes in App.js.

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import SignUp from './SignUp';
import SignIn from './SignIn';
import Home from './Home';

function App() {
return (
<Router>
<Switch>
<Route path="/signup" component={SignUp} />
<Route path="/signin" component={SignIn} />
<Route path="/" component={Home} />
</Switch>
</Router>
);
}

export default App;

Here's what your App.js file might look like with routes set up for signing in, signing up, and the home page, including all the updates for handling the Firebase authentication state.

import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
import firebase from './firebase';
import SignUp from './SignUp'; // Import your SignUp component
import SignIn from './SignIn'; // Import your SignIn component
import Home from './Home'; // Import your Home component

function App() {
const [currentUser, setCurrentUser] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
// Listen for auth state changes
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
setCurrentUser(user);
setLoading(false);
});

// Clean up the listener on unmount
return unsubscribe;
}, []);

if (loading) {
// Possibly include some loading indicator while waiting for auth state
return <div>Loading...</div>;
}

return (
<Router>
<Switch>
<Route exact path="/" render={() => (
currentUser ? <Home /> : <Redirect to="/signin" />
)} />
<Route path="/signup" render={() => (
!currentUser ? <SignUp /> : <Redirect to="/" />
)} />
<Route path="/signin" render={() => (
!currentUser ? <SignIn /> : <Redirect to="/" />
)} />
{/* Redirect all other paths to the home page, update as needed */}
<Route render={() => <Redirect to="/" />} />
</Switch>
</Router>
);
}

export default App;
Explanation of code:
We use useState to keep track of the currentUser and a loading state to handle the asynchronous nature of Firebase authentication status checking.
The useEffect hook sets up a listener for Firebase auth state changes. If the user is logged in, the currentUser state is updated, and when the user logs out, it's set to null.
The loading state is used to display a loading indicator until the Firebase auth state is confirmed.
We use BrowserRouter (Router) and Switch from react-router-dom to define our routing.
The Route components determine which page to display based on the URL path.
We use render props to conditionally render Redirect components depending on whether the user is authenticated, ensuring users can only access certain pages if they are signed in or not.
This setup uses conditional rendering within Route's render prop to redirect users based on authentication state.
The loading state allows you to put a loading screen or spinner until the authentication state is known, avoiding flickering of components which require user to be authenticated.
Remember to replace './firebase' with the path to your Firebase configuration file where Firebase is initialized, and ensure that the SignUp, SignIn, and Home components are correctly implemented as per the instructions I have previously outlined.

Step 9: Running the App

To run the app, use this command:
npm start
Your app should be running on http://localhost:3000.

Step 10: Deploy

When you're ready to deploy, use Firebase Hosting.
Install the Firebase CLI.
npm install -g firebase-tools
Log in to Firebase with the CLI.
firebase login
Initialize your Firebase project.
firebase init
Make sure to select the correct Firebase project.
For public directory, use build.
Select to configure as a single-page app, answering yes.
Build your app for production.
npm run build
Deploy your app.
firebase deploy
Your app should now be live!

Notes:

Always keep your firebaseConfig secure and avoid pushing sensitive keys to public repositories.
Adapt the authentication flow to your application needs. You may want to use context or Redux to manage the auth state globally.
Add error handling and loading states where appropriate.
Customize the components to suit your application's design and requirements.
Testing should be done locally before deploying your application.


app.js:

import React, { useEffect, useState } from 'react'; import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom'; import { getAuth, onAuthStateChanged } from 'firebase/auth'; import SignIn from './SignIn'; // Import SignIn component import SignUp from './SignUp'; // Import SignUp component import Home from './Home'; // Import Home component import { firebaseApp } from './firebase'; // Import the initialized firebaseApp

function App() { const [currentUser, setCurrentUser] = useState(null); const [loading, setLoading] = useState(true);
useEffect(() => { // Get the Firebase Auth instance const auth = getAuth(firebaseApp);

// Subscribe to the authentication state
const unsubscribe = onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
setLoading(false);
});

// Unsubscribe from the listener when unmounting
return () => unsubscribe();
}, []);
if (loading) { return <div>Loading...</div>; // Or a spinner/loading component }
return ( <Router> <Routes> <Route path="/" element={currentUser ? <Home /> : <Navigate to="/signin" replace />} /> <Route path="/signup" element={!currentUser ? <SignUp /> : <Navigate to="/" replace />} /> <Route path="/signin" element={!currentUser ? <SignIn /> : <Navigate to="/" replace />} /> {/* Redirect all unknown paths to the Home page or a 404 component /} <Route path="" element={<Navigate to="/" replace />} /> </Routes> </Router> ); }
export default App;

app.js file. This file is a React component that serves as the main application entry point for a web application using Firebase for authentication and React Router for navigation.
Here's a brief explanation of what each part of the code does:
Imports: The file starts by importing necessary modules and components like React hooks (useEffect, useState), router components (Router, Route, Routes, Navigate), Firebase authentication functions (getAuth, onAuthStateChanged), and custom components (SignIn, SignUp, Home). It also imports the initialized Firebase app (firebaseApp).
Function App: This is a functional component that defines the main app structure.
State Variables: It uses two state variables currentUser and loading. currentUser will hold the authenticated user object if a user is logged in, otherwise, it's null. loading is a boolean used for rendering a loading message/spinner while Firebase determines the authentication state.
useEffect Hook: It runs the provided function when the component mounts (equivalent to componentDidMount in class components). It establishes a subscription to the authentication state using onAuthStateChanged. When the authentication state changes (a user logs in or out), the state variables are updated accordingly (setCurrentUser(user) sets the authenticated user, and setLoading(false) hides the loading message). The useEffect hook also returns a cleanup function to unsubscribe from the auth listener when the component unmounts.
Conditional Rendering: If loading is true, it renders a "Loading..." message. Once loading is false, it proceeds to render the router and routes.
React Router: The Router encloses the Routes component which defines the routing for the application.
Route Definitions: There are routes defined for the root path /, /signup, and /signin, with conditional rendering based on the currentUser's state. If the user is authenticated (currentUser is truthy), they are redirected to the Home component when they try to navigate to /signin or /signup. Unauthenticated users (currentUser is null) are redirected to the SignIn component if they try to access the root path /.
Wildcard Route: A wildcard route (*) is defined to handle any unknown paths by redirecting users to the root path /, which could be replaced with a 404 not found component if desired.
This file sets up a basic protected route system, managing access to the Home, SignIn, and SignUp components based on the user's authentication state with Firebase. The routing ensures that the user is directed to the appropriate component, whether they need to log in, sign up, or if they're already authenticated, see the content of the Home component.


authservice.js:
import { getAuth, createUserWithEmailAndPassword, signInWithEmailAndPassword, signOut as firebaseSignOut } from 'firebase/auth'; import { firebaseApp } from './firebase'; // Ensure firebaseApp is correctly configured and exported from 'firebase.js'
const auth = getAuth(firebaseApp);
export const signUp = (email, password) => { return createUserWithEmailAndPassword(auth, email, password); };
export const signIn = (email, password) => { return signInWithEmailAndPassword(auth, email, password); };
export const signOut = () => { return firebaseSignOut(auth); };

The authservice.js file you provided contains functions for managing user authentication in a web application using Firebase. It exports three functions: signUp, signIn, and signOut, which are meant to be used for user registration, logging in, and logging out, respectively. Below is the breakdown of its contents:
Imports: The file begins by importing authentication functions from Firebase's firebase/auth module. It imports createUserWithEmailAndPassword, signInWithEmailAndPassword, and signOut (renamed as firebaseSignOut to avoid naming conflicts). It also imports the firebaseApp.
Firebase Auth Instance: An instance of Firebase Authentication is created by calling getAuth(firebaseApp). This requires the firebaseApp to be initialized with Firebase configuration and exported from the firebase.js file.
signUp Function:
This function is an exported asynchronous function that takes email and password as arguments.
It calls createUserWithEmailAndPassword provided by Firebase to create a new user with the email and password.
It returns a promise that resolves with the user credential if the registration is successful, or it rejects with an error if there's a problem (e.g., the email is already in use).
signIn Function:
Similar to signUp, this function also takes email and password as arguments and is asynchronous.
It uses the signInWithEmailAndPassword Firebase function to sign in a user with the provided email and password.
It returns a promise that resolves with the user credential on successful sign-in or rejects with an error on failure (e.g., wrong password).
signOut Function:
This function doesn't take any arguments and is responsible for signing out the currently signed-in user.
It calls firebaseSignOut (Firebase's signOut function) passing in the auth instance.
It returns a promise that resolves when the sign-out is successful or rejects with an error on failure.
Each exported function is a wrapper around Firebase Authentication API calls, abstracting the Firebase details away from the rest of the application. This modularizes the authentication logic, making it easier to maintain and test independently from other parts of the application. Components needing to perform authentication-related tasks can import these functions and use them as required.


firebase.js:
// Correctly import your Firebase modules import { initializeApp } from 'firebase/app';
const firebaseConfig = { apiKey: "AIzaSyDnvrBo6novkjRirEZgKbyUsZ8PV_KmJtw", authDomain: "nov21-c5a93.firebaseapp.com", projectId: "nov21-c5a93", storageBucket: "nov21-c5a93.appspot.com", messagingSenderId: "1067036450668", appId: "1:1067036450668:web:99af75d7aa4f1b29d21d49" };
export const firebaseApp = initializeApp(firebaseConfig);


The firebase.js file contains the configuration and initialization code for a Firebase application. When you're building an application that uses Firebase services such as authentication, database, or storage, you must provide it with your project's specific settings. This configuration allows your app to communicate securely with Firebase services.
Here's a breakdown of the code and the steps involved:
Firebase Module Import: The file imports the initializeApp function from the firebase/app module. This function is used to initialize the Firebase application with your project-specific settings.
Firebase Configuration: firebaseConfig is a plain JavaScript object containing keys and values that uniquely identify your Firebase project. This includes your API key, the project ID, the authentication domain, storage bucket, messaging sender ID, and application ID among other settings.
apiKey: A public identifier for your app, but non-sensitive (you often include this in your client-side code).
authDomain: A domain used for authenticating users.
projectId: The ID of your Firebase project.
storageBucket: The URL of your Firebase project's storage bucket.
messagingSenderId: The ID for sending messages through Firebase Cloud Messaging.
appId: The unique identifier for your app within Firebase.
Firebase Initialization: The configuration object is then passed to initializeApp() to create and initialize an instance of the Firebase app linked to your provided config.
Exporting Initialized Firebase App: The initialized Firebase app object (firebaseApp) is exported so that it can be imported and used in other files of your application to interact with Firebase services.
It's important to keep your firebaseConfig secure, especially the apiKey, to prevent unauthorized use. While the apiKey is not a secret and can be included in public client-side code, it's meant to identify your project on Google servers, not for access control. Abuse of the API key can create security vulnerabilities (e.g., someone could set up their own application to consume resources on your quota).
Note that you've posted your entire Firebase config publicly here, which includes sensitive project information. As a best practice, you should avoid sharing these details in public forums or with unauthorized individuals. If you suspect that the integrity of your Firebase config has been compromised, you should consider regenerating your API key from the Firebase console and updating your application's configuration.

Home.js:
import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { getAuth, onAuthStateChanged } from 'firebase/auth';
function Home() { const [currentUser, setCurrentUser] = useState(null); const navigate = useNavigate();
useEffect(() => { const auth = getAuth(); const unsubscribe = onAuthStateChanged(auth, (user) => { if (user) { // User is signed in. setCurrentUser(user); } else { // No user is signed in; navigate('/signin'); } }); return unsubscribe; // Make sure to unsubscribe on component unmount }, [navigate]);
const handleSignOut = async () => { const auth = getAuth(); try { await auth.signOut(); navigate('/signin'); } catch (error) { console.error('Sign out failed', error); alert('Sign out failed: ' + error.message); } };
if (!currentUser) { return <div>Loading...</div>; }
return ( <div> <h1>Home</h1> <p>Welcome, {currentUser.email}!</p> <button onClick={handleSignOut}>Sign Out</button> </div> ); }
export default Home;


The Home.js file is a React component that is intended to serve as the home page of a web application where the user lands after successfully signing in. This component is responsible for displaying the user's email address and providing a sign-out button. Here's how it works:
Imports:
It imports the necessary React hooks (useEffect, useState), the useNavigate hook from react-router-dom for programmatically navigating between routes, and Firebase authentication functions (getAuth, onAuthStateChanged).
Home Component:
State Variable: There's a single state variable currentUser that holds the currently authenticated user's object, initialized as null.
useNavigate Hook: The useNavigate is used to obtain a function that allows the component to change the current location programmatically.
useEffect Hook: It sets up an effect that checks the authentication state of the user when the component mounts:
Firebase Auth Instance: It gets the Firebase Auth instance by calling getAuth().
Auth State Listener: It subscribes to onAuthStateChanged, which is a Firebase listener that invokes a callback function with the user object every time the authentication state changes.
If a user is logged in (user is truthy), it sets currentUser to the logged-in user's object.
If there is no logged-in user (user is falsy), it redirects to the sign-in page via navigate('/signin').
The effect returns a function that unsubscribes from the auth listener to clean up the effect when the component unmounts.
Sign Out Handler:
handleSignOut Function: This is an asynchronous function that handles the user's sign-out process.
It gets the Auth instance and calls signOut on it, which is an asynchronous operation.
After successfully signing out, it redirects the user to the sign-in page.
If the sign-out process encounters an error, it logs the error and alerts the user about the failure.
Conditional Rendering:
Loading: If currentUser is null, it renders a loading message, indicating that it's waiting for the authentication state to finalize.
Authenticated User View: If currentUser is not null, it renders a welcome message showing the currentUser.email, and a sign-out button. Clicking the button will trigger the handleSignOut function.
Export:
The Home component is exported as default, allowing it to be imported and used in other parts of the application.
In summary, Home.js is a protected route that only shows content to authenticated users. If authentication is not confirmed, it redirects the user to the sign-in page. It provides a personalized message and a way to sign out of the application.


Signin.js:
import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { signIn } from './AuthService';
function SignIn() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const navigate = useNavigate();
const handleSignIn = async (event) => { event.preventDefault(); try { await signIn(email, password); // Redirect to home upon sign in navigate('/'); } catch (error) { // Handle errors here, such as showing a notification to the user console.error("Failed to sign in", error); alert("Failed to sign in: " + error.message); } };
return ( <div> <h2>Sign In</h2> <form onSubmit={handleSignIn}> <div> <label>Email</label> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required /> </div>

<div>
<label>Password</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.