---
title: "Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 7"
description: "Nối tiếp phần 6 của series, trong phần này, chúng ta sẽ bắt tay vào code Sign Up page,config lại phần router để đáp ứng được vấn đề Authentication kèm theo tạo custom hook để listening for Authentication. Không dài dòng nữa, Chúng ta bắt đầu thôi 😁."
canonical: "https://200lab.io/blog/netflix-clone-voi-reactjs-styled-components-va-firebase-firestore-auth-phan-7"
published: "2021-12-08T01:00:00Z"
updated: "2026-08-21T07:17:41Z"
authors: ["TỪ QUỐC HƯNG"]
tags: ["Javascript"]
reading_minutes: 8
access: "public"
---

Nối tiếp phần 6 của series, trong phần này, chúng ta sẽ bắt tay vào code **Sign Up page**, **config **lại phần **router **để đáp ứng được vấn đề **Authentication** kèm theo tạo **custom hook** để listening for Authentication. Không dài dòng nữa, Chúng ta bắt đầu thôi 😁.

# I. Tạo sign up page.

Thì **Sign up page** của Netflix thì sẽ trông như thế nào? Thì hiện tại cái flow của việc Sign Up tài khoản mới của Netflix thì nó nhiều step hơn rồi. Chúng ta không nhất thiết phải làm y hệt như nó, do đó chúng ta sẽ code **Sign Up page** như **Sign In page**, ta chỉ thêm bớt một số thứ thôi.

Chúng ta làm như sau:

Trong file `/pages/signup.jsx`, chúng ta sẽ sử dụng lại những gì mà chúng ta đã built-in là component **form **và **headercontainer**.

```jsx
import React, { useState, useContext } from "react";
import { useHistory } from "react-router-dom";
import { FirebaseContext } from "../context/firebase";
import { HeaderContainer, FooterContainer } from "../containers/index";
import { Form } from "../components/index";
import * as ROUTES from "../constants/routes";

export default function SignUp() {
	const { firebase } = useContext(FirebaseContext);
	const history = useHistory();

	const [firstName, setFirstName] = useState("");
	const [email, setEmail] = useState("");
	const [password, setPassword] = useState("");
	const [errorMessage, setErrorMessage] = useState("");

	// Check form input elements are valid
	const isInvalid = firstName === "" || password === "" || email === "";

	const handleSignUp = (event) => {
		event.preventDefault();

		firebase
			.auth()
			.createUserWithEmailAndPassword(email, password)
			.then((res) => {
				res.user
					.updateProfile({
						displayName: firstName,
						photoURL: Math.floor(Math.random() * 5) + 1
					})
					.then(() => {
						history.push(ROUTES.BROWSE);
					});
			})
			.catch((error) => {
				setErrorMessage(error.code);
			});
	};
	return (
		<>
			<HeaderContainer status="hidden">
				<Form>
					<Form.Title>Sign Up</Form.Title>
					{errorMessage && (
						<Form.ErrorMessage>{errorMessage}</Form.ErrorMessage>
					)}
					<Form.Base onSubmit={handleSignUp} method="POST">
						<Form.Input
							placeholder="First name"
							value={firstName}
							onChange={({ target }) =>
								setFirstName(target.value)
							}
						/>
						<Form.Input
							type="email"
							placeholder="Your email"
							value={email}
							onChange={({ target }) => setEmail(target.value)}
						/>
						<Form.Input
							type="password"
							placeholder="Your password"
							value={password}
							onChange={({ target }) => setPassword(target.value)}
						/>
						<Form.Button disabled={isInvalid} type="submit">
							Sign Up
						</Form.Button>
						<Form.FlexBox style={{ justifyContent: "flex-end" }}>
							<Form.SmallLink to="/">Need help?</Form.SmallLink>
						</Form.FlexBox>
					</Form.Base>
					<Form.Wrapper
						style={{
							flexDirection: "column",
							alignItems: "flex-start"
						}}
					>
						<Form.SocialButton>
							<Form.Image src="./images/icons/FB-Logo.png" />
							<Form.SmallLink to="/">
								Login with Facebook
							</Form.SmallLink>
						</Form.SocialButton>
						<Form.Text style={{ marginBottom: "0" }}>
							Already a user?{" "}
							<Form.RedirectLink to={ROUTES.SIGN_IN}>
								Sign in now
							</Form.RedirectLink>
							.
						</Form.Text>
						<Form.SmallText>
							This page is protected by Google reCAPTCHA to ensure
							you're not a bot.{" "}
							<Form.SmallLink to="/" style={{ color: "#0071eb" }}>
								Learn more
							</Form.SmallLink>
							.
						</Form.SmallText>
					</Form.Wrapper>
				</Form>
			</HeaderContainer>
			<FooterContainer />
		</>
	);
}
```

_signup.jsx_

- Sau khi code xong và run thử thì ta sẽ được như hình.

![UI of Sign Up page](https://assets.200lab.io/images/2024/12/05/image-39-7d5d33724fa9e2ee3d11.webp)

Giải thích một chút về function `handleSignUp()` nhé:

- Sau khi **send request** gồm `email` và `password`, nếu đăng ký thành công thì sẽ sang bước tiếp theo.
- Sau khi đăng ký thành công sẽ tiến hành **update profile** gồm `displayName` và `photoURL` (Avatar) của user.
- Phần `photoURL` mình chỉ cho random 1 trong tổng số 5 image mà mình có cho user khi đăng ký thôi 😁.

Nếu chưa có ảnh để làm avatar thì các bạn có thể tải [tại đây](<https://drive.google.com/drive/folders/14V2o_Gffi7mFPUh_IVMtuCN5_KznNfkQ?usp=sharing>).

Chúng ta cùng test thử nó có thể Sign Up một user mới được không nhé 😉.

- Nhập thông tin đầy đủ.

![In case of full information to register](https://assets.200lab.io/images/2024/12/05/image-40-386b477aa46eeeb90d44.webp)

- Nếu Sign Up thành công thì nó sẽ tự động **redirect** sang **Browse page**.

![Sign Up Success](https://assets.200lab.io/images/2024/12/05/image-41-3a54d1b82d92b1642999.webp)

- Để chắc chắn hơn, chúng ta có thể kiểm tra trên firebase, như hình là đã Sign Up thành công .

![Account has been successfully registered - Firebase](https://assets.200lab.io/images/2024/12/05/image-42-38b8b402c71d4ef886d8.webp)

# II. Cấu hình Router auth.

Tại sao lại config lại phần router? Thì như các bạn biết, việc chia các loại router khác nhau để phân loại các role của user, tức là ta sẽ phân loại ra thành 2 loại user là: user chưa login và user đã login.

Tùy thuộc vào từng case mà user sẽ vào được những page khác nhau, đó là những gì mà chúng ta sẽ config ngay dưới đây. Trong folder **src** ta tạo thêm folder **helpers** và file `routes.js` như sau:

![helpers folder](https://assets.200lab.io/images/2024/12/05/image-43-ac7eceb7d79d4ada99df.webp)

Trong file `routes.js` ta thêm các đoạn code sau:

```jsx
import React from "react";
import { Route, Redirect } from "react-router-dom";

export function IsUser({ user, LoggedInPath, children, ...rest }) {
	return (
		<Route
			{...rest}
			render={() => {
				if (!user) {
					return children;
				}
				if (user) {
					return <Redirect to={{ pathname: LoggedInPath }} />;
				}
				return null;
			}}
		/>
	);
}

export function ProtectedRoute({ user, children, ...rest }) {
	return (
		<Route
			{...rest}
			render={({ location }) => {
				if (user) {
					return children;
				}
				if (!user) {
					return (
						<Redirect
							to={{
								pathname: "signin",
								state: { from: location }
							}}
						/>
					);
				}

				return null;
			}}
		/>
	);
}
```

_helpers/routes.js_

Giải thích một chút về 2 function trên nhé:

- `isUser()`: function này có nhiệm vụ là check xem user đã login hay chưa, nếu chưa thì sẽ load page tương ứng là **SignIn** hoặc **SignUp page** ngược lại sẽ **Redirect** đến **Browser page**.
- `ProtectedRoute()`: function này có nhiệm vụ là chặn những user có những action cố truy cập **Browser page** hoặc những page khác mà bắt buộc user phải login tài khoản mới có thể truy cập được. Nếu user vẫn cố tình truy cập các page này bằng cách gõ các **path** tương ứng trên thanh **url** của browser thì nó sẽ tự động **Redirect** đến **SignIn page** theo `pathname` và `location` tương ứng.

Okie! Vậy sử dụng nó ra sao? Thì để sử dụng, chúng ta sẽ **import** và dùng nó trong file `App.js` như sau:

```jsx
import React from "react";
import { BrowserRouter } from "react-router-dom";
import * as ROUTES from "./constants/routes";
import { Home, SignIn, SignUp, Browse } from "./pages/index";
import { IsUser, ProtectedRoute } from "./helpers/routes"; //<------------ import here

export default function App() {
	const user = null;
	return (
		<BrowserRouter>
			{/*Use it here*/}
			<IsUser
				user={user}
				path={ROUTES.SIGN_IN}
				LoggedInPath={ROUTES.BROWSE}
			>
				<SignIn />
			</IsUser>
			<IsUser
				user={user}
				path={ROUTES.SIGN_UP}
				LoggedInPath={ROUTES.BROWSE}
			>
				<SignUp />
			</IsUser>
			<ProtectedRoute user={user} path={ROUTES.BROWSE} exact>
				<Browse />
			</ProtectedRoute>
			<IsUser user={user} path={ROUTES.HOME} LoggedInPath={ROUTES.BROWSE}>
				<Home />
			</IsUser>
		</BrowserRouter>
	);
}
```

_App.js_

# III. Tạo custom hook (auth listener).

Chúng ta sẽ tạo một custom hook để handle việc listen user SignIn hoặc SignOut từ đó có thể lấy được data của user đó để sử dụng cho những việc cần thiết sau này.

Trong folder **src** ta tạo một folder **hooks**, dùng để chứa các custom hook. Tiếp theo trong folder **hooks** vừa tạo, ta tạo thêm file `useAuthListener.jsx` và sẽ code như sau:

```jsx
import React, { useState, useContext, useEffect } from "react";
import { FirebaseContext } from "../context/firebase";

export default function useAuthListener() {
	const [user, setUser] = useState(
		JSON.parse(localStorage.getItem("authUser"))
	);
	const { firebase } = useContext(FirebaseContext);

	useEffect(() => {
		const listener = firebase.auth().onAuthStateChanged((authUser) => {
			if (authUser) {
				localStorage.setItem("authUser", JSON.stringify(authUser));
				setUser(authUser);
			} else {
				localStorage.removeItem("authUser");
				setUser(null);
			}
		});
		return () => listener();
	}, [firebase]);
	return { user };
}
```

_useAuthListener.jsx_

Giải thích:

- `onAuthStateChanged()`: function này listen khi user trigger việc **SignIn** hoặc **SignOut**.
- Nếu user SignIn thì lưu user data vào **localStorage** ngược lại thì remove khỏi **localStorage**.

Vì trong tương lai, chúng ta sẽ có thêm nhiều custom hook khác, do đó ta sẽ tạo thêm file `index.jsx` trong folder **hooks** và config như sau:

```jsx
import useAuthListener from "./useAuthListener";

export { useAuthListener };
```

_hooks/index.jsx_

Tiếp theo là sử dụng custom hook vừa built xong nè 😁, trong file `App.js` chúng ta sẽ sử dụng như sau:

```jsx
import React from "react";
import { BrowserRouter } from "react-router-dom";
import * as ROUTES from "./constants/routes";
import { Home, SignIn, SignUp, Browse } from "./pages/index";
import { IsUser, ProtectedRoute } from "./helpers/routes";
import { useAuthListener } from "./hooks/index"; //<---------- Import here

export default function App() {
	const { user } = useAuthListener(); //<---------- Use it here
	return (
		<BrowserRouter>
			<IsUser
				user={user}
				path={ROUTES.SIGN_IN}
				LoggedInPath={ROUTES.BROWSE}
			>
				<SignIn />
			</IsUser>
			<IsUser
				user={user}
				path={ROUTES.SIGN_UP}
				LoggedInPath={ROUTES.BROWSE}
			>
				<SignUp />
			</IsUser>
			<ProtectedRoute user={user} path={ROUTES.BROWSE} exact>
				<Browse />
			</ProtectedRoute>
			<IsUser user={user} path={ROUTES.HOME} LoggedInPath={ROUTES.BROWSE}>
				<Home />
			</IsUser>
		</BrowserRouter>
	);
}
```

_App.js_

Sau khi reload thì có thể các bạn sẽ thắc mắc là tại sao current page là **Browser page** (như hình dưới), mặc dù chúng ta đã thực hiện việc **SignIn** đâu?

![current page - Browser page](https://assets.200lab.io/images/2024/12/05/image-44-068341641186d285c32e.webp)

Trong bài trước chúng ta đã test việc **SigIn** và **SignUp**, do đó account mà chúng ta đã **SignUp** và sử dụng để **SignIn** ở bài trước nó vẫn còn lưu lại trên firebase.

Tuy nhiên trên firebase nó chưa ghi nhận việc chúng ta đã gọi function `signOut()` của nó để **SignOut** cái account này ra khỏi app, do đó ta mới có trường hợp trên.

Okie current page là **Browser page** do đó ta có thể thấy custom hook của chúng ta đã hoạt động. Chúng ta check thêm nó có lưu được vào **localStorage** không nhé 😉.

![authUser - localStorage](https://assets.200lab.io/images/2024/12/05/image-45-36f5fa4abf35ccccb39e.webp)

Perfect!!! 😁

# IV. Tổng kết.

Okie phần thứ 7 của series này cũng đã xong, cảm ơn các bạn đã đọc. Hẹn gặp lại các bạn trong phần tiếp theo nhé 😉. See u again~
