React - Facebook Login Tutorial & Example
Tutorial built with React 17.0.0
Other versions available:
- Angular: Angular 14, 10
- Vue: Vue 3, Vue 2
In this tutorial we'll cover how to implement Facebook Login in React with an example app that allows you to login with Facebook and view/update/delete accounts registered in the React app.
The first time you log into the React app with Facebook, an account is registered that is associated with your Facebook Id so the app can identify you when you login again with Facebook. The account is created with the name from your Facebook account and an extraInfo
field with some default text, both the name and extra info can be updated in the React app, and updating account details only changes them in the app it doesn't affect anything on Facebook.
The example project is available on GitHub at https://github.com/cornflourblue/react-facebook-login-example.
Here it is in action: (See on StackBlitz at https://stackblitz.com/edit/react-facebook-login-example)
React Facebook Login App Details
The example app contains the following three routes (pages) to demonstrate logging in with Facebook, viewing accounts and updating account details:
- Login (
/login
) - contains a Facebook login button that triggers authentication with Facebook and registration/authentication with the React app. - Home (
/
) - displays a list of all accounts in the React app with buttons to edit or delete any of them. - Edit Account (
/edit/:id
) - contains a form to update the specified account details.
Facebook App is required for Facebook Login
To integrate Facebook Login into a website or application you need to create a Facebook App at https://developers.facebook.com/apps/ and set up the Facebook Login product under the App. Creating a Facebook App will provide you with a Facebook App ID which is required when initializing the Facebook JavaScript SDK (FB.init(...)
). For more info see the Facebook Login docs at https://developers.facebook.com/docs/facebook-login/web.
The example React app uses a Facebook App named JasonWatmore.com Login Example that I created for this tutorial (App ID: 314930319788683). The Facebook App ID is located in the dotenv file (/.env
) in the example, environment variables set in the dotenv file that are prefixed with REACT_APP_
are accessible in the React app via process.env.<variable name>
(e.g. process.env.REACT_APP_FACEBOOK_APP_ID
). For more info on using environment variables in React see https://create-react-app.dev/docs/adding-custom-environment-variables/.
Fake backend API
The example React app runs with a fake backend api by default to enable it to run completely in the browser without a real api (backend-less), the fake api contains routes for authentication and account CRUD operations and it uses browser local storage to save data. To disable the fake backend you just have to remove a couple of lines of code from the index.js file, you can refer to the fake-backend to see what's required to build a real api for the example.
Updates only affect data in the React app
Updating or deleting account information in the React app will only change the data saved in the app, it won't (and can't) change anything in the associated Facebook account.
Authentication flow with Facebook access tokens and JWT tokens
Authentication is implemented with Facebook access tokens and JWT tokens. On successful login to Facebook an access token is returned to the React app, which is then used to authenticate with the api (or fake backend) which returns a short lived JWT token that expires after 15 minutes. The JWT is used for accessing secure routes on the api, and the Facebook access token is used to re-authenticate with the api to get a new JWT token when (or just before) it expires. The React app starts a timer to re-authenticate for a new JWT token 1 minute before it expires to keep the account logged in, this is done in the apiAuthenticate()
method of the account service.
Run the React Facebook Login App Locally
- Install Node.js and npm from https://nodejs.org
- Download or clone the project source code from https://github.com/cornflourblue/react-facebook-login-example
- Install all required npm packages by running
npm install
from the command line in the project root folder (where the package.json is located). - Start the application in SSL (https) mode by running
npm start
from the command line in the project root folder, SSL is required for the Facebook SDK to run properly, this will launch a browser with the URLhttps://localhost:3000/
. SSL mode is set in the dotenv file (/.env
) with the config variableHTTPS=true
. - You should see the message Your connection is not private (or something similar in non Chrome browsers), this is nothing to worry about it's just because the React development server runs with a self signed SSL certificate. To open the app click the "Advanced" button and the link "Proceed to localhost".
React Project Structure
Create React App was used to generate the base project structure with the npx create-react-app <project name>
command, the tool is also used to build and serve the application. For more info about Create React App see https://create-react-app.dev/.
Each feature has it's own folder (home & login), other shared/common code such as services, helpers, components etc are placed in folders prefixed with an underscore _
to easily differentiate them from features and group them together at the top of the folder structure.
The index.js
files in the shared/non-feature folders are barrel files that group the exported modules from a folder together so they can be imported using the folder path instead of the full module path and to enable importing multiple modules in a single import (e.g. import { initFacebookSdk, jwtInterceptor, errorInterceptor, history } from './_helpers';
).
Here are the main project files that contain the application logic.
- public
- src
- _components
- Nav.jsx
- PrivateRoute.jsx
- index.js
- _helpers
- _services
- account.service.js
- index.js
- home
- login
- App.jsx
- index.css
- index.js
- _components
- .env
- jsconfig.json
- package.json
Main Index Html File
The main index.html file is the initial page loaded by the browser that kicks everything off. Create React App (with Webpack under the hood) bundles all of the compiled javascript files together and injects them into the body of the index.html page so the scripts can be loaded and executed by the browser.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React - Facebook Login Example</title>
<!-- bootstrap & font-awesome css -->
<link href="//netdna.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" />
<link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"></div>
</body>
</html>
Nav Component
The nav component displays the primary bar in the example. The component subscribes to the accountService.account
observable and only displays the nav if the user is logged in.
The react router NavLink
component automatically adds the active
class to the active nav item so it is highlighted in the UI.
import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
import { accountService } from '_services';
function Nav() {
const [account, setAccount] = useState(null);
useEffect(() => {
accountService.account.subscribe(x => setAccount(x));
}, []);
// only show nav when logged in
if (!account) return null;
return (
<nav className="navbar navbar-expand navbar-dark bg-dark">
<div className="navbar-nav">
<NavLink exact to="/" className="nav-item nav-link">Home</NavLink>
<button className="btn btn-link nav-item nav-link" onClick={accountService.logout}>Logout</button>
</div>
</nav>
);
}
export { Nav };
Private Route
The react private route component renders a route component if the user is logged in, if the user isn't logged in they're redirected to the /login
page with the return url in the location state property.
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { accountService } from '_services';
function PrivateRoute({ component: Component, ...rest }) {
return (
<Route {...rest} render={props => {
const account = accountService.accountValue;
if (!account) {
// not logged in so redirect to login page with the return url
return <Redirect to={{ pathname: '/login', state: { from: props.location } }} />
}
// authorized so return component
return <Component {...props} />
}} />
);
}
export { PrivateRoute };
Error Interceptor
The Error Interceptor intercepts http responses from the api to check if there were any errors. All errors are logged to the console and if there is a 401 Unauthorized
or 403 Forbidden
response the account is automatically logged out of the application.
It's implemented as an axios response interceptor, by passing callback functions to axios.interceptors.response.use()
you can intercept responses before they are handled by then()
or catch()
. The first callback function intercepts successful responses and the second callback function intercepts error responses. For more info on axios interceptors see https://github.com/axios/axios#interceptors.
The error interceptor is initialized on app startup in the index.js file.
import axios from 'axios';
import { accountService } from '_services';
export function errorInterceptor() {
axios.interceptors.response.use(null, (error) => {
const { response } = error;
if (!response) {
// network error
console.error(error);
return;
}
if ([401, 403].includes(response.status) && accountService.accountValue) {
// auto logout if 401 or 403 response returned from api
accountService.logout();
}
const errorMessage = response.data?.message || response.statusText;
console.error('ERROR:', errorMessage);
});
}
Fake Backend
In order to run and test the React app without a real backend API, the example uses a fake backend that intercepts the HTTP requests from the React app and sends back "fake" responses. This is done by monkey patching the axios request methods (get, post, put, delete) to return fake responses for a specific set of routes.
Monkey patching is a technique used to alter the behaviour of an existing function either to extend it or change the way it works. In JavaScript this is done by storing a reference to the original function in a variable and replacing the original function with a new custom function that (optionally) calls the original function before/after executing some custom code.
The fake backend is organised into a top level handleRoute()
function that checks the request url and method to determine how the request should be handled. For fake routes one of the below // route functions
is called, for all other routes the request is passed through to the real backend by calling the original axios request function (axios[`original${method}`](url, body())
). Below the route functions there are // helper functions
for returning different response types and performing small tasks.
import axios from 'axios';
import { accountService } from '_services';
// array in local storage for accounts
const accountsKey = 'react-facebook-login-accounts';
let accounts = JSON.parse(localStorage.getItem(accountsKey)) || [];
export function fakeBackend() {
const methods = ['get', 'post', 'put', 'delete'];
methods.forEach(method => {
axios[`original${method}`] = axios[method];
axios[method] = function (url, ...params) {
return new Promise((resolve, reject) => {
return handleRoute();
function handleRoute() {
switch (true) {
case url.endsWith('/accounts/authenticate') && method === 'post':
return authenticate();
case url.endsWith('/accounts') && method === 'get':
return getAccounts();
case url.match(/\/accounts\/\d+$/) && method === 'get':
return getAccountById();
case url.match(/\/accounts\/\d+$/) && method === 'put':
return updateAccount();
case url.match(/\/accounts\/\d+$/) && method === 'delete':
return deleteAccount();
default:
// pass through any requests not handled above
return axios[`original${method}`](url, body())
.then(response => resolve(response))
.catch(error => reject(error));
}
}
// route functions
function authenticate() {
const { accessToken } = body();
axios.get(`https://graph.facebook.com/v8.0/me?access_token=${accessToken}`)
.then(response => {
const { data } = response;
if (data.error) return unauthorized(data.error.message);
let account = accounts.find(x => x.facebookId === data.id);
if (!account) {
// create new account if first time logging in
account = {
id: newAccountId(),
facebookId: data.id,
name: data.name,
extraInfo: `This is some extra info about ${data.name} that is saved in the API`
}
accounts.push(account);
localStorage.setItem(accountsKey, JSON.stringify(accounts));
}
return ok({
...account,
token: generateJwtToken(account)
});
});
}
function getAccounts() {
if (!isLoggedIn()) return unauthorized();
return ok(accounts);
}
function getAccountById() {
if (!isLoggedIn()) return unauthorized();
let account = accounts.find(x => x.id === idFromUrl());
return ok(account);
}
function updateAccount() {
if (!isLoggedIn()) return unauthorized();
let params = body();
let account = accounts.find(x => x.id === idFromUrl());
// update and save account
Object.assign(account, params);
localStorage.setItem(accountsKey, JSON.stringify(accounts));
return ok(account);
}
function deleteAccount() {
if (!isLoggedIn()) return unauthorized();
// delete account then save
accounts = accounts.filter(x => x.id !== idFromUrl());
localStorage.setItem(accountsKey, JSON.stringify(accounts));
return ok();
}
// helper functions
function ok(body) {
// wrap in timeout to simulate server api call
setTimeout(() => resolve({ status: 200, data: body }), 500);
}
function unauthorized() {
setTimeout(() => {
const response = { status: 401, data: { message: 'Unauthorized' } };
reject(response);
// manually trigger error interceptor
const errorInterceptor = axios.interceptors.response.handlers[0].rejected;
errorInterceptor({ response });
}, 500);
}
function isLoggedIn() {
return accountService.accountValue;
}
function idFromUrl() {
const urlParts = url.split('/');
return parseInt(urlParts[urlParts.length - 1]);
}
function body() {
if (['post', 'put'].includes(method))
return params[0];
}
function newAccountId() {
return accounts.length ? Math.max(...accounts.map(x => x.id)) + 1 : 1;
}
function generateJwtToken(account) {
// create token that expires in 15 minutes
const tokenPayload = {
exp: Math.round(new Date(Date.now() + 15*60*1000).getTime() / 1000),
id: account.id
}
return `fake-jwt-token.${btoa(JSON.stringify(tokenPayload))}`;
}
});
}
});
}
History
The history helper creates the browser history object used by the react app, it is passed to the Router
component in the main index.js file and enables us to access the history object outside of react components, for example from the logout()
method of the account service.
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
Init Facebook SDK
The init Facebook SDK function runs before the React app starts up in index.js, it loads and initializes the Facebook SDK and gets the user's login status from Facebook. If the user is already logged in with Facebook they are automatically logged into the React app using the Facebook access token and taken to the home page, otherwise the app starts normally and displays the login page.
import { accountService } from '_services';
const facebookAppId = process.env.REACT_APP_FACEBOOK_APP_ID;
export function initFacebookSdk() {
return new Promise(resolve => {
// wait for facebook sdk to initialize before starting the react app
window.fbAsyncInit = function () {
window.FB.init({
appId: facebookAppId,
cookie: true,
xfbml: true,
version: 'v8.0'
});
// auto authenticate with the api if already logged in with facebook
window.FB.getLoginStatus(({ authResponse }) => {
if (authResponse) {
accountService.apiAuthenticate(authResponse.accessToken).then(resolve);
} else {
resolve();
}
});
};
// load facebook sdk script
(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) { return; }
js = d.createElement(s); js.id = id;
js.src = "https://connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
});
}
JWT Interceptor
The JWT Interceptor intercepts http requests from the application to add a JWT auth token to the Authorization header if the user is logged in and the request is to the React app's api url (process.env.REACT_APP_API_URL
).
It's implemented as an axios request interceptor, by passing a callback function to axios.interceptors.request.use()
you can intercept requests before they get sent to the server. For more info on axios interceptors see https://github.com/axios/axios#interceptors.
The jwt interceptor is initialized on app startup in the index.js file.
import axios from 'axios';
import { accountService } from '_services';
export function jwtInterceptor() {
axios.interceptors.request.use(request => {
// add auth header with jwt if account is logged in and request is to the api url
const account = accountService.accountValue;
const isLoggedIn = account?.token;
const isApiUrl = request.url.startsWith(process.env.REACT_APP_API_URL);
if (isLoggedIn && isApiUrl) {
request.headers.common.Authorization = `Bearer ${account.token}`;
}
return request;
});
}
Account Service
The account service handles communication between the React app and the backend api for everything related to accounts. It contains methods for logging in and out, as well as standard CRUD methods for retrieving and modifying account data.
The login()
method first logs into Facebook (await new Promise(FB.login)
), then passes the Facebook access token to the apiAuthenticate()
method to login to the api (or fake backend).
On successful login the api returns the account details and a JWT token which are published to all subscriber components with the call to accountSubject.next(account)
in the apiAuthenticate()
method. The method then starts a countdown timer by calling startAuthenticateTimer()
to auto refresh the JWT token in the background (silent refresh) one minute before it expires in order to keep the account logged in.
The logout()
method revokes the Facebook App's permissions with FB.api('/me/permissions', 'delete')
then logs out of Facebook by calling FB.logout()
, revoking permissions is required to completely logout because FB.logout()
doesn't remove the FB auth cookie so the user is logged back in on page refresh. The logout()
method then cancels the silent refresh running in the background by calling stopAuthenticateTimer()
, logs the user out of the React app by publishing a null value to all subscriber components (accountSubject.next(null)
) and redirects to the login page.
The account
property exposes an RxJS observable (Observable<Account>
) so any component can subscribe to be notified when a user logs in, logs out, has their token refreshed or updates their account. The notification is triggered by the call to accountSubject.next()
from each of the corresponding methods in the service. For more info on component communication with RxJS see React Hooks + RxJS - Communicating Between Components with Observable & Subject.
import { BehaviorSubject } from 'rxjs';
import axios from 'axios';
import { history } from '_helpers';
const baseUrl = `${process.env.REACT_APP_API_URL}/accounts`;
const accountSubject = new BehaviorSubject(null);
export const accountService = {
login,
apiAuthenticate,
logout,
getAll,
getById,
update,
delete: _delete,
account: accountSubject.asObservable(),
get accountValue () { return accountSubject.value; }
};
async function login() {
// login with facebook then authenticate with the API to get a JWT auth token
const { authResponse } = await new Promise(window.FB.login);
if (!authResponse) return;
await apiAuthenticate(authResponse.accessToken);
// get return url from location state or default to home page
const { from } = history.location.state || { from: { pathname: "/" } };
history.push(from);
}
async function apiAuthenticate(accessToken) {
// authenticate with the api using a facebook access token,
// on success the api returns an account object with a JWT auth token
const response = await axios.post(`${baseUrl}/authenticate`, { accessToken });
const account = response.data;
accountSubject.next(account);
startAuthenticateTimer();
return account;
}
function logout() {
// revoke app permissions to logout completely because FB.logout() doesn't remove FB cookie
window.FB.api('/me/permissions', 'delete', null, () => window.FB.logout());
stopAuthenticateTimer();
accountSubject.next(null);
history.push('/login');
}
function getAll() {
return axios.get(baseUrl)
.then(response => response.data);
}
function getById(id) {
return axios.get(`${baseUrl}/${id}`)
.then(response => response.data);
}
async function update(id, params) {
const response = await axios.put(`${baseUrl}/${id}`, params);
let account = response.data;
// update the current account if it was updated
if (account.id === accountSubject.value?.id) {
// publish updated account to subscribers
account = { ...accountSubject.value, ...account };
accountSubject.next(account);
}
return account;
}
async function _delete(id) {
await axios.delete(`${baseUrl}/${id}`);
if (id === accountSubject.value?.id) {
// auto logout if the logged in account was deleted
logout();
}
}
// helper methods
let authenticateTimeout;
function startAuthenticateTimer() {
// parse json object from base64 encoded jwt token
const jwtToken = JSON.parse(atob(accountSubject.value.token.split('.')[1]));
// set a timeout to re-authenticate with the api one minute before the token expires
const expires = new Date(jwtToken.exp * 1000);
const timeout = expires.getTime() - Date.now() - (60 * 1000);
const { accessToken } = window.FB.getAuthResponse();
authenticateTimeout = setTimeout(() => apiAuthenticate(accessToken), timeout);
}
function stopAuthenticateTimer() {
// cancel timer for re-authenticating with the api
clearTimeout(authenticateTimeout);
}
Edit Account Component
The EditAccount
component is used for editing an account, it contains a form built with the React Hook Form library.
The useForm()
hook function returns an object with methods for working with a form including registering inputs, handling form submit, setting input values, accessing form state and more, for a complete list see https://react-hook-form.com/api#useForm.
The onSubmit
function gets called when the form is submitted and valid, and updates the account using the account service.
The returned JSX template contains the form with all of the input fields. The form fields are registered with the React Hook Form using the ref={register}
attribute which registers each input with the input name
.
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { useForm } from "react-hook-form";
import { accountService } from '_services';
function EditAccount({ history, match }) {
const { id } = match.params;
// functions to build form returned by useForm() hook
const { register, handleSubmit, setValue, formState } = useForm();
// pre-populate form with account details when component loads
const [account, setAccount] = useState(null);
useEffect(() => {
accountService.getById(id).then(account => {
setAccount(account);
const fields = ['name', 'extraInfo'];
fields.forEach(field => setValue(field, account[field]));
});
}, [id, setValue]);
// form submit handler
const [error, setError] = useState('');
function onSubmit(data) {
return accountService.update(id, data)
.then(() => history.push('..'))
.catch(err => setError(err));
}
return (
<div>
<h2>Edit Account</h2>
<p>Updating the information here will only change it inside this application, it won't (and can't) change anything in the associated Facebook account.</p>
{account &&
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group">
<label>Facebook Id</label>
<div>{account.facebookId}</div>
</div>
<div className="form-group">
<label>Name</label>
<input name="name" type="text" ref={register} className="form-control" />
</div>
<div className="form-group">
<label>Extra Info</label>
<input name="extraInfo" type="text" ref={register} className="form-control" />
</div>
<div className="form-group">
<button type="submit" disabled={formState.isSubmitting} className="btn btn-primary">
{formState.isSubmitting &&
<span className="spinner-border spinner-border-sm mr-1"></span>
}
Save
</button>
<Link to=".." className="btn btn-link">Cancel</Link>
{error &&
<div className="alert alert-danger mt-3 mb-0">{error}</div>
}
</div>
</form>
}
{!account &&
<div className="text-center p-3">
<span className="spinner-border spinner-border-lg align-center"></span>
</div>
}
</div>
);
}
export { EditAccount };
Home Component
The home component gets all accounts from the account service in the useEffect()
react hooks function and makes them available to the JSX template via the accounts
state property.
The deleteAccount()
function sets the property isDeleting
to true for the specified account so the template displays a spinner on the delete button, then calls accountService.delete(id)
to delete the account and removes the deleted account from component accounts
array so it is removed from the UI.
The returned JSX template contains a simple welcome message and a list of all accounts with buttons for editing or deleting.
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { accountService } from '_services';
function Home() {
const [mounted, setMounted] = useState(false);
const [accounts, setAccounts] = useState(null);
useEffect(() => {
setMounted(true);
accountService.getAll().then(x => setAccounts(x));
return () => setMounted(false);
}, []);
function deleteAccount(id) {
// set isDeleting flag to show spinner
setAccounts(accounts.map(x => {
if (x.id === id) { x.isDeleting = true; }
return x;
}));
// delete account
accountService.delete(id).then(() => {
if (mounted) {
setAccounts(accounts.filter(x => x.id !== id));
}
});
}
return (
<div>
<h2>You're logged in with React & Facebook!!</h2>
<p>All accounts from secure api end point:</p>
<table className="table table-striped">
<thead>
<tr>
<th>Id</th>
<th>Facebook Id</th>
<th>Name</th>
<th>Extra Info</th>
<th></th>
</tr>
</thead>
<tbody>
{accounts && accounts.map(account =>
<tr key={account.id}>
<td>{account.id}</td>
<td>{account.facebookId}</td>
<td>{account.name}</td>
<td>{account.extraInfo}</td>
<td className="text-right" style={{ whiteSpace: 'nowrap' }}>
<Link to={`edit/${account.id}`} className="btn btn-sm btn-primary mr-1">Edit</Link>
<button onClick={() => deleteAccount(account.id)} className="btn btn-sm btn-danger btn-delete-account" disabled={account.isDeleting}>
{account.isDeleting
? <span className="spinner-border spinner-border-sm"></span>
: <span>Delete</span>
}
</button>
</td>
</tr>
)}
{!accounts &&
<tr>
<td colSpan="5" className="text-center">
<span className="spinner-border spinner-border-lg align-center"></span>
</td>
</tr>
}
</tbody>
</table>
</div>
);
}
export { Home };
Login Component
The login component enables users to login to the React app with Facebook, if the user is already logged in they are automatically redirected to the home page by the useEffect()
react hooks function.
The returned JSX template contains a single Facebook login button that is bound to the accountService.login()
method on click.
import React, { useEffect } from 'react';
import { accountService } from '_services';
function Login({ history }) {
useEffect(() => {
// redirect to home if already logged in
if (accountService.accountValue) {
history.push('/');
}
}, [history]);
return (
<div className="col-md-6 offset-md-3 mt-5 text-center">
<div className="card">
<h4 className="card-header">React - Facebook Login Example</h4>
<div className="card-body">
<button className="btn btn-facebook" onClick={accountService.login}>
<i className="fa fa-facebook mr-1"></i>
Login with Facebook
</button>
</div>
</div>
</div>
);
}
export { Login };
App Component
The App
component is the root component of the example app, it contains the outer html, main nav and routes for the application.
The first route (<Redirect from="/:url*(/+)" to={pathname.slice(0, -1)} />) automatically removes trailing slashes from URLs which can cause issues and are a side-effect of using relative react router links. For more info see React Router - Relative Links Example.
The home and edit account routes are secured by the private route component, and the login route is public.
The last route (<Redirect from="*" to="/" />) is a catch-all redirect route that redirects any unmatched paths to the home page.
import React from 'react';
import { Route, Switch, Redirect, useLocation } from 'react-router-dom';
import { Nav, PrivateRoute } from '_components';
import { Home } from 'home/Home';
import { EditAccount } from 'home/EditAccount';
import { Login } from 'login/Login';
function App() {
const pathname = useLocation().pathname || '';
return (
<div>
<Nav />
<div className="container pt-4">
<Switch>
<Redirect from="/:url*(/+)" to={pathname.slice(0, -1)} />
<PrivateRoute exact path="/" component={Home} />
<PrivateRoute path="/edit/:id" component={EditAccount} />
<Route path="/login" component={Login} />
<Redirect from="*" to="/" />
</Switch>
</div>
</div>
);
}
export { App };
Global CSS Styles
The global stylesheet file contains CSS styles that are applied globally throughout the React application.
a { cursor: pointer }
.btn-facebook {
background: #3B5998;
color: #fff;
}
.btn-facebook:hover {
color: #fff;
opacity: 0.8;
}
.btn-delete-account {
width: 40px;
text-align: center;
box-sizing: content-box;
}
Main index.js file
The main index.js file bootstraps the React Facebook application by rendering the App
component (wrapped in a Router
) into the app
div element defined in the main index html file.
Before starting the React app it imports the global CSS stylesheet into the application, enables the fake backend api, enables the jwt interceptor and error interceptor, and waits for the Facebook SDK to load and initialize.
To disable the fake backend simply remove the 2 lines below the comment // setup fake backend
.
import React from 'react';
import { Router } from 'react-router-dom';
import { render } from 'react-dom';
// global stylesheet
import './index.css';
import { initFacebookSdk, jwtInterceptor, errorInterceptor, history } from './_helpers';
import { App } from './App';
// setup fake backend
import { fakeBackend } from './_helpers';
fakeBackend();
// enable interceptors for http requests
jwtInterceptor();
errorInterceptor();
// wait for facebook sdk before startup
initFacebookSdk().then(startApp);
function startApp() {
render(
<Router history={history}>
<App />
</Router>,
document.getElementById('app')
);
}
dotenv
The dotenv file contains environment variables used in the example React app.
The app is configured to use HTTPS (SSL) with the setting HTTPS=true
, this is required by the Facebook SDK.
Environment variables set in the dotenv file that are prefixed with REACT_APP_
are accessible in the React app via process.env.<variable name>
(e.g. process.env.REACT_APP_FACEBOOK_APP_ID
). For more info on using environment variables in React see https://create-react-app.dev/docs/adding-custom-environment-variables/
HTTPS=true
REACT_APP_API_URL=http://localhost:4000
REACT_APP_FACEBOOK_APP_ID=314930319788683
jsconfig.json
The below configuration enables support for absolute imports to the application, so modules can be imported with absolute paths instead of relative paths (e.g. import { MyComponent } from '_components';
instead of import { MyComponent } from '../../../_components';
).
For more info on absolute imports in React see https://create-react-app.dev/docs/importing-a-component/#absolute-imports.
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}
Package.json
The package.json file contains project configuration information including package dependencies that get installed when you run npm install
and scripts that are executed when you run npm start
or npm run build
etc. Full documentation is available at https://docs.npmjs.com/files/package.json.
{
"name": "react-facebook-login-example",
"version": "0.1.0",
"dependencies": {
"axios": "^0.20.0",
"history": "^4.10.1",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-hook-form": "^6.9.5",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.4",
"rxjs": "^6.6.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Need Some React Help?
Search fiverr for freelance React developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!