├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
└── src
├── components
├── Account
│ └── index.js
├── App
│ ├── index.css
│ └── index.js
├── Home
│ └── index.js
├── Landing
│ └── index.js
├── Navigation
│ └── index.js
├── PasswordChange
│ └── index.js
├── PasswordForget
│ └── index.js
├── Session
│ ├── withAuthentication.js
│ └── withAuthorization.js
├── SignIn
│ └── index.js
├── SignOut
│ └── index.js
└── SignUp
│ └── index.js
├── constants
└── routes.js
├── firebase
├── auth.js
├── db.js
├── firebase.js
└── index.js
├── index.css
├── index.js
└── registerServiceWorker.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-firebase-authentication
2 |
3 | [](https://slack-the-road-to-learn-react.wieruch.com/)
4 |
5 | * Found in [Taming the State in React](https://roadtoreact.com/course-details?courseId=TAMING_THE_STATE)
6 | * [Live](https://react-firebase-authentication.wieruch.com/)
7 |
8 | ## Features
9 |
10 | * uses:
11 | * only React (create-react-app)
12 | * firebase 4.3.1
13 | * react-router 4.2.0
14 | * no Redux/MobX
15 | * features:
16 | * Sign In
17 | * Sign Up
18 | * Sign Out
19 | * Password Forget
20 | * Password Change
21 | * Protected Routes with Authorization
22 | * Database: Users
23 |
24 | ## Installation
25 |
26 | * `git clone git@github.com:rwieruch/react-firebase-authentication.git`
27 | * `cd react-firebase-authentication`
28 | * `npm install`
29 | * `npm start`
30 | * visit http://localhost:3000/
31 | * Use your own Firebase Credentials
32 |
33 | ### Use your own Firebase Credentials
34 |
35 | * visit https://firebase.google.com/ and create a Firebase App
36 | * copy and paste your Credentials from your Firebase App into src/firebase/firebase.js
37 | * activate Email/Password Sign-In Method in your Firebase App
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-firebase-authentication",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "firebase": "^4.3.1",
7 | "prop-types": "^15.5.10",
8 | "react": "^16.1.1",
9 | "react-dom": "^16.1.1",
10 | "react-router-dom": "^4.2.2",
11 | "react-scripts": "1.0.17"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rwieruch/react-firebase-authentication-chat/05db06ccbc8a3ecdd0421ffc4afaca9496953694/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/Account/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import { PasswordForgetForm } from '../PasswordForget';
5 | import PasswordChangeForm from '../PasswordChange';
6 | import withAuthorization from '../Session/withAuthorization';
7 |
8 | const AccountPage = (props, { authUser }) =>
9 |
10 |
Account: {authUser.email}
11 |
12 |
13 |
14 |
15 | AccountPage.contextTypes = {
16 | authUser: PropTypes.object,
17 | };
18 |
19 | const authCondition = (authUser) => !!authUser;
20 |
21 | export default withAuthorization(authCondition)(AccountPage);
--------------------------------------------------------------------------------
/src/components/App/index.css:
--------------------------------------------------------------------------------
1 | .app {
2 | margin: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/App/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | BrowserRouter as Router,
4 | Route,
5 | } from 'react-router-dom';
6 |
7 | import Navigation from '../Navigation';
8 | import LandingPage from '../Landing';
9 | import SignUpPage from '../SignUp';
10 | import SignInPage from '../SignIn';
11 | import PasswordForgetPage from '../PasswordForget';
12 | import HomePage from '../Home';
13 | import AccountPage from '../Account';
14 | import withAuthentication from '../Session/withAuthentication';
15 | import * as routes from '../../constants/routes';
16 |
17 | import './index.css';
18 |
19 | const App = () =>
20 |
21 |
37 |
38 |
39 | export default withAuthentication(App);
--------------------------------------------------------------------------------
/src/components/Home/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import withAuthorization from '../Session/withAuthorization';
5 | import { db } from '../../firebase';
6 |
7 | class HomePage extends Component {
8 | constructor() {
9 | super();
10 |
11 | this.state = {
12 | value: '',
13 | messages: [],
14 | users: {},
15 | };
16 |
17 | this.onAddMessage = this.onAddMessage.bind(this);
18 | this.onChangeMessage = this.onChangeMessage.bind(this);
19 | }
20 |
21 | componentWillMount() {
22 | db.onceGetUsers().then(snapshot =>
23 | this.setState(() => ({ users: snapshot.val() }))
24 | );
25 |
26 | db.onMessageAdded((snapshot) => {
27 | this.setState(prevState => ({
28 | messages: [ snapshot.val(), ...prevState.messages ],
29 | }));
30 | });
31 | }
32 |
33 | onChangeMessage(event) {
34 | const { value } = event.target;
35 | this.setState(() => ({ value }));
36 | }
37 |
38 | onAddMessage(event) {
39 | event.preventDefault();
40 |
41 | const { authUser } = this.context;
42 | const { value } = this.state;
43 |
44 | db.doCreateMessage(authUser.uid, value);
45 |
46 | this.setState(() => ({ value: '' }));
47 | }
48 |
49 | render() {
50 | const {
51 | messages,
52 | users,
53 | value,
54 | } = this.state;
55 |
56 | return (
57 |
58 |
59 | {messages.map((message, key) =>
60 |
65 | )}
66 |
67 |
68 |
76 |
77 | );
78 | }
79 | }
80 |
81 | const Message = ({ message, user }) => {
82 | const messenger = user
83 | ? `${user.username}:`
84 | : '???';
85 |
86 | return {messenger} {message.text};
87 | }
88 |
89 | HomePage.contextTypes = {
90 | authUser: PropTypes.object,
91 | };
92 |
93 | const authCondition = (authUser) => !!authUser;
94 |
95 | export default withAuthorization(authCondition)(HomePage);
--------------------------------------------------------------------------------
/src/components/Landing/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const LandingPage = () =>
4 |
5 |
Landing
6 |
The Landing Page is open to everyone, even though the user isn't signed in.
7 |
8 |
9 | export default LandingPage;
10 |
--------------------------------------------------------------------------------
/src/components/Navigation/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Link } from 'react-router-dom';
4 |
5 | import SignOutButton from '../SignOut';
6 | import * as routes from '../../constants/routes';
7 |
8 | const Navigation = (props, { authUser }) =>
9 |
10 | { authUser
11 | ?
12 | :
13 | }
14 |
15 |
16 | Navigation.contextTypes = {
17 | authUser: PropTypes.object,
18 | };
19 |
20 | const NavigationAuth = () =>
21 |
22 | - Landing
23 | - Home
24 | - Account
25 |
26 |
27 |
28 | const NavigationNonAuth = () =>
29 |
30 | - Landing
31 | - Sign In
32 |
33 |
34 | export default Navigation;
35 |
--------------------------------------------------------------------------------
/src/components/PasswordChange/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import { auth } from '../../firebase';
4 |
5 | const updateByPropertyName = (propertyName, value) => () => ({
6 | [propertyName]: value,
7 | });
8 |
9 | const INITIAL_STATE = {
10 | passwordOne: '',
11 | passwordTwo: '',
12 | error: null,
13 | };
14 |
15 | class PasswordChangeForm extends Component {
16 | constructor(props) {
17 | super(props);
18 |
19 | this.state = { ...INITIAL_STATE };
20 | }
21 |
22 | onSubmit = (event) => {
23 | const { passwordOne } = this.state;
24 |
25 | auth.doPasswordUpdate(passwordOne)
26 | .then(() => {
27 | this.setState(() => ({ ...INITIAL_STATE }));
28 | })
29 | .catch(error => {
30 | this.setState(updateByPropertyName('error', error));
31 | });
32 |
33 | event.preventDefault();
34 | }
35 |
36 | render() {
37 | const {
38 | passwordOne,
39 | passwordTwo,
40 | error,
41 | } = this.state;
42 |
43 | const isInvalid =
44 | passwordOne !== passwordTwo ||
45 | passwordOne === '';
46 |
47 | return (
48 |
67 | );
68 | }
69 | }
70 |
71 | export default PasswordChangeForm;
--------------------------------------------------------------------------------
/src/components/PasswordForget/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | import { auth } from '../../firebase';
5 | import * as routes from '../../constants/routes';
6 |
7 | const PasswordForgetPage = () =>
8 |
9 |
PasswordForget
10 |
11 |
12 |
13 | const updateByPropertyName = (propertyName, value) => () => ({
14 | [propertyName]: value,
15 | });
16 |
17 | const INITIAL_STATE = {
18 | email: '',
19 | error: null,
20 | };
21 |
22 | class PasswordForgetForm extends Component {
23 | constructor(props) {
24 | super(props);
25 |
26 | this.state = { ...INITIAL_STATE };
27 | }
28 |
29 | onSubmit = (event) => {
30 | const { email } = this.state;
31 |
32 | auth.doPasswordReset(email)
33 | .then(() => {
34 | this.setState(() => ({ ...INITIAL_STATE }));
35 | })
36 | .catch(error => {
37 | this.setState(updateByPropertyName('error', error));
38 | });
39 |
40 | event.preventDefault();
41 | }
42 |
43 | render() {
44 | const {
45 | email,
46 | error,
47 | } = this.state;
48 |
49 | const isInvalid = email === '';
50 |
51 | return (
52 |
65 | );
66 | }
67 | }
68 |
69 | const PasswordForgetLink = () =>
70 |
71 | Forgot Password?
72 |
73 |
74 | export default PasswordForgetPage;
75 |
76 | export {
77 | PasswordForgetForm,
78 | PasswordForgetLink,
79 | };
80 |
--------------------------------------------------------------------------------
/src/components/Session/withAuthentication.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import { firebase } from '../../firebase';
5 |
6 | const withAuthentication = (Component) => {
7 | class WithAuthentication extends React.Component {
8 | constructor(props) {
9 | super(props);
10 |
11 | this.state = {
12 | authUser: null,
13 | };
14 | }
15 |
16 | getChildContext() {
17 | return {
18 | authUser: this.state.authUser,
19 | };
20 | }
21 |
22 | componentDidMount() {
23 | firebase.auth.onAuthStateChanged(authUser => {
24 | authUser
25 | ? this.setState(() => ({ authUser }))
26 | : this.setState(() => ({ authUser: null }));
27 | });
28 | }
29 |
30 | render() {
31 | return (
32 |
33 | );
34 | }
35 | }
36 |
37 | WithAuthentication.childContextTypes = {
38 | authUser: PropTypes.object,
39 | };
40 |
41 | return WithAuthentication;
42 | }
43 |
44 | export default withAuthentication;
--------------------------------------------------------------------------------
/src/components/Session/withAuthorization.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { withRouter } from 'react-router-dom';
4 |
5 | import { firebase } from '../../firebase';
6 | import * as routes from '../../constants/routes';
7 |
8 | const withAuthorization = (condition) => (Component) => {
9 | class WithAuthorization extends React.Component {
10 | componentDidMount() {
11 | firebase.auth.onAuthStateChanged(authUser => {
12 | if (!condition(authUser)) {
13 | this.props.history.push(routes.SIGN_IN);
14 | }
15 | });
16 | }
17 |
18 | render() {
19 | return this.context.authUser ? : null;
20 | }
21 | }
22 |
23 | WithAuthorization.contextTypes = {
24 | authUser: PropTypes.object,
25 | };
26 |
27 | return withRouter(WithAuthorization);
28 | }
29 |
30 | export default withAuthorization;
--------------------------------------------------------------------------------
/src/components/SignIn/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { withRouter } from 'react-router-dom';
3 |
4 | import { SignUpLink } from '../SignUp';
5 | import { PasswordForgetLink } from '../PasswordForget';
6 | import { auth } from '../../firebase';
7 | import * as routes from '../../constants/routes';
8 |
9 | const SignInPage = ({ history }) =>
10 |
11 |
SignIn
12 |
13 |
14 |
15 |
16 |
17 | const updateByPropertyName = (propertyName, value) => () => ({
18 | [propertyName]: value,
19 | });
20 |
21 | const INITIAL_STATE = {
22 | email: '',
23 | password: '',
24 | error: null,
25 | };
26 |
27 | class SignInForm extends Component {
28 | constructor(props) {
29 | super(props);
30 |
31 | this.state = { ...INITIAL_STATE };
32 | }
33 |
34 | onSubmit = (event) => {
35 | const {
36 | email,
37 | password,
38 | } = this.state;
39 |
40 | const {
41 | history,
42 | } = this.props;
43 |
44 | auth.doSignInWithEmailAndPassword(email, password)
45 | .then(() => {
46 | this.setState(() => ({ ...INITIAL_STATE }));
47 | history.push(routes.HOME);
48 | })
49 | .catch(error => {
50 | this.setState(updateByPropertyName('error', error));
51 | });
52 |
53 | event.preventDefault();
54 | }
55 |
56 | render() {
57 | const {
58 | email,
59 | password,
60 | error,
61 | } = this.state;
62 |
63 | const isInvalid =
64 | password === '' ||
65 | email === '';
66 |
67 | return (
68 |
87 | );
88 | }
89 | }
90 |
91 | export default withRouter(SignInPage);
92 |
93 | export {
94 | SignInForm,
95 | };
96 |
--------------------------------------------------------------------------------
/src/components/SignOut/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { auth } from '../../firebase';
4 |
5 | const SignOutButton = () =>
6 |
12 |
13 | export default SignOutButton;
14 |
--------------------------------------------------------------------------------
/src/components/SignUp/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | Link,
4 | withRouter,
5 | } from 'react-router-dom';
6 |
7 | import { auth, db } from '../../firebase';
8 | import * as routes from '../../constants/routes';
9 |
10 | const SignUpPage = ({ history }) =>
11 |
12 |
SignUp
13 |
14 |
15 |
16 | const updateByPropertyName = (propertyName, value) => () => ({
17 | [propertyName]: value,
18 | });
19 |
20 | const INITIAL_STATE = {
21 | username: '',
22 | email: '',
23 | passwordOne: '',
24 | passwordTwo: '',
25 | error: null,
26 | };
27 |
28 | class SignUpForm extends Component {
29 | constructor(props) {
30 | super(props);
31 |
32 | this.state = { ...INITIAL_STATE };
33 | }
34 |
35 | onSubmit = (event) => {
36 | const {
37 | username,
38 | email,
39 | passwordOne,
40 | } = this.state;
41 |
42 | const {
43 | history,
44 | } = this.props;
45 |
46 | auth.doCreateUserWithEmailAndPassword(email, passwordOne)
47 | .then(authUser => {
48 |
49 | // Create a user in your own accessible Firebase Database too
50 | db.doCreateUser(authUser.uid, username, email)
51 | .then(() => {
52 | this.setState(() => ({ ...INITIAL_STATE }));
53 | history.push(routes.HOME);
54 | })
55 | .catch(error => {
56 | this.setState(updateByPropertyName('error', error));
57 | });
58 |
59 | })
60 | .catch(error => {
61 | this.setState(updateByPropertyName('error', error));
62 | });
63 |
64 | event.preventDefault();
65 | }
66 |
67 | render() {
68 | const {
69 | username,
70 | email,
71 | passwordOne,
72 | passwordTwo,
73 | error,
74 | } = this.state;
75 |
76 | const isInvalid =
77 | passwordOne !== passwordTwo ||
78 | passwordOne === '' ||
79 | username === '' ||
80 | email === '';
81 |
82 | return (
83 |
114 | );
115 | }
116 | }
117 |
118 | const SignUpLink = () =>
119 |
120 | Don't have an account?
121 | {' '}
122 | Sign Up
123 |
124 |
125 | export default withRouter(SignUpPage);
126 |
127 | export {
128 | SignUpForm,
129 | SignUpLink,
130 | };
--------------------------------------------------------------------------------
/src/constants/routes.js:
--------------------------------------------------------------------------------
1 | export const LANDING = '/';
2 | export const SIGN_UP = '/signup';
3 | export const SIGN_IN = '/signin';
4 | export const PASSWORD_FORGET = '/pw-forget';
5 | export const HOME = '/home';
6 | export const ACCOUNT = '/account';
7 |
--------------------------------------------------------------------------------
/src/firebase/auth.js:
--------------------------------------------------------------------------------
1 | import { auth } from './firebase';
2 |
3 | // Sign Up
4 | export const doCreateUserWithEmailAndPassword = (email, password) =>
5 | auth.createUserWithEmailAndPassword(email, password);
6 |
7 | // Sign In
8 | export const doSignInWithEmailAndPassword = (email, password) =>
9 | auth.signInWithEmailAndPassword(email, password);
10 |
11 | // Sign out
12 | export const doSignOut = () =>
13 | auth.signOut();
14 |
15 | // Password Reset
16 | export const doPasswordReset = (email) =>
17 | auth.sendPasswordResetEmail(email);
18 |
19 | // Password Change
20 | export const doPasswordUpdate = (password) =>
21 | auth.currentUser.updatePassword(password);
22 |
--------------------------------------------------------------------------------
/src/firebase/db.js:
--------------------------------------------------------------------------------
1 | import { db } from './firebase';
2 |
3 | // User API
4 |
5 | export const doCreateUser = (id, username, email) =>
6 | db.ref(`users/${id}`).set({
7 | username,
8 | email,
9 | });
10 |
11 | export const onceGetUsers = () =>
12 | db.ref('users').once('value');
13 |
14 | // Chat API
15 |
16 | export const doCreateMessage = (userId, text) =>
17 | db.ref('messages').push({
18 | userId,
19 | text,
20 | });
21 |
22 | export const onMessageAdded = (callback) =>
23 | db.ref('messages')
24 | .orderByKey()
25 | .limitToLast(100)
26 | .on('child_added', callback);
27 |
28 |
--------------------------------------------------------------------------------
/src/firebase/firebase.js:
--------------------------------------------------------------------------------
1 | import * as firebase from 'firebase';
2 |
3 | const prodConfig = {
4 | apiKey: YOUR_API_KEY,
5 | authDomain: YOUR_AUTH_DOMAIN,
6 | databaseURL: YOUR_DATABASE_URL,
7 | projectId: YOUR_PROJECT_ID,
8 | storageBucket: '',
9 | messagingSenderId: YOUR_MESSAGING_SENDER_ID,
10 | };
11 |
12 | const devConfig = {
13 | apiKey: YOUR_API_KEY,
14 | authDomain: YOUR_AUTH_DOMAIN,
15 | databaseURL: YOUR_DATABASE_URL,
16 | projectId: YOUR_PROJECT_ID,
17 | storageBucket: '',
18 | messagingSenderId: YOUR_MESSAGING_SENDER_ID,
19 | };
20 |
21 | const config = process.env.NODE_ENV === 'production'
22 | ? prodConfig
23 | : devConfig;
24 |
25 | if (!firebase.apps.length) {
26 | firebase.initializeApp(config);
27 | }
28 |
29 | const db = firebase.database();
30 | const auth = firebase.auth();
31 |
32 | export {
33 | db,
34 | auth,
35 | };
36 |
--------------------------------------------------------------------------------
/src/firebase/index.js:
--------------------------------------------------------------------------------
1 | import * as auth from './auth';
2 | import * as db from './db';
3 | import * as firebase from './firebase';
4 |
5 | export {
6 | auth,
7 | db,
8 | firebase,
9 | };
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './components/App';
5 | import registerServiceWorker from './registerServiceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 | registerServiceWorker();
9 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (!isLocalhost) {
36 | // Is not local host. Just register service worker
37 | registerValidSW(swUrl);
38 | } else {
39 | // This is running on localhost. Lets check if a service worker still exists or not.
40 | checkValidServiceWorker(swUrl);
41 | }
42 | });
43 | }
44 | }
45 |
46 | function registerValidSW(swUrl) {
47 | navigator.serviceWorker
48 | .register(swUrl)
49 | .then(registration => {
50 | registration.onupdatefound = () => {
51 | const installingWorker = registration.installing;
52 | installingWorker.onstatechange = () => {
53 | if (installingWorker.state === 'installed') {
54 | if (navigator.serviceWorker.controller) {
55 | // At this point, the old content will have been purged and
56 | // the fresh content will have been added to the cache.
57 | // It's the perfect time to display a "New content is
58 | // available; please refresh." message in your web app.
59 | console.log('New content is available; please refresh.');
60 | } else {
61 | // At this point, everything has been precached.
62 | // It's the perfect time to display a
63 | // "Content is cached for offline use." message.
64 | console.log('Content is cached for offline use.');
65 | }
66 | }
67 | };
68 | };
69 | })
70 | .catch(error => {
71 | console.error('Error during service worker registration:', error);
72 | });
73 | }
74 |
75 | function checkValidServiceWorker(swUrl) {
76 | // Check if the service worker can be found. If it can't reload the page.
77 | fetch(swUrl)
78 | .then(response => {
79 | // Ensure service worker exists, and that we really are getting a JS file.
80 | if (
81 | response.status === 404 ||
82 | response.headers.get('content-type').indexOf('javascript') === -1
83 | ) {
84 | // No service worker found. Probably a different app. Reload the page.
85 | navigator.serviceWorker.ready.then(registration => {
86 | registration.unregister().then(() => {
87 | window.location.reload();
88 | });
89 | });
90 | } else {
91 | // Service worker found. Proceed as normal.
92 | registerValidSW(swUrl);
93 | }
94 | })
95 | .catch(() => {
96 | console.log(
97 | 'No internet connection found. App is running in offline mode.'
98 | );
99 | });
100 | }
101 |
102 | export function unregister() {
103 | if ('serviceWorker' in navigator) {
104 | navigator.serviceWorker.ready.then(registration => {
105 | registration.unregister();
106 | });
107 | }
108 | }
109 |
--------------------------------------------------------------------------------