, December 05, 2021

0 kết quả được tìm thấy

Hướng dẫn clone instagram với React JS và Firebase phần 4.


  •   13 min reads
Hướng dẫn clone instagram với React JS và Firebase phần 4.

Hello các bạn, trong phần 4 này chúng ta sẽ cùng nhau tìm hiểu và sử dụng các phương thức mà firebase cung cấp cho chúng ta để có thể đăng ký một tài khoản, sử dụng nó để Sign In vào app của mình 😁 nào chúng ta bắt đầu thôi 😉

I. Setup authentication với Firebase.

User authentication hay xác thực người dùng, đây là quá trình định danh (hay xác định) một tài khoản đang login vào hệ thống nó có phải thuộc hệ thống đó hay không. Đây là bước khá quan trọng trong một hệ thống mà có yếu tố người dùng.

Bình thường chúng ta chỉ có thể xem các bài post có sẵn của những user khác mà thôi, chúng ta không thể thao tác nào khác với các bài post đó. Lúc này chúng ta cần phải tạo một tài khoản, sau đó sử dụng nó để Sign In vào app của chúng ta, khi đó chúng ta có thể tạo mới hoặc tương tác với các bài post đó.

Đầu tiên để setup authentication, ta cần mở thanh navigation ra và chọn Authentication

Authentication in Navbar

Sau đó bạn sẽ thấy giao diện như hình và nhấn vào button Set up sign-in method hoặc nhấn vào tab Sign-in method như hình nhé. Nếu nó hiện ra cho bạn một background màu hồng "nam tính" thì bạn sẽ thấy button Get started ở đó, hãy nhấn vào nhé 😁 thì nó sẽ hiện ra phần Sign-in method luôn cho bạn 😉.

Sign-in method

Sau khi chọn như mình hướng dẫn thì ta sẽ thấy toàn bộ những cách thứ mà firebase cung cấp cho chúng ta để có thể Authentication. Ở phần này mình sẽ sử dụng phương thức Authentication bằng Email/Password nha, các bạn có thể sử dụng những cái khác cũng được cũng không sao cả nếu bạn biết nhé 😉.

Sign-in providers - Email/Password

Chọn nó và tick vào để Enable nó lên và nhấn Save.

Enable method

II. Tạo form SignIn và SignUp với Material-UI

Chúng ta sẽ sử dụng phần modal được tạo sẵn từ Material UI nhé, bạn có thể tham khảo code của mình ở bên dưới. Đây là file App.js, mình có copy một số code của phần modal mà Material UI cung cấp.

import React, { useState, useEffect } from "react";
import "./App.css";
import Header from "./components/Header/Header";
import PostItem from "./components/PostItem/PostItem";
import { db } from "./firebaseConfig";
import { collection, getDocs } from "firebase/firestore";
import { makeStyles } from "@material-ui/core/styles";
import Modal from "@material-ui/core/Modal";


function getModalStyle() {
	const top = 50;
	const left = 50;

	return {
		top: `${top}%`,
		left: `${left}%`,
		transform: `translate(-${top}%, -${left}%)`,
	};
}

const useStyles = makeStyles((theme) => ({
	paper: {
		position: "absolute",
		width: 400,
		backgroundColor: theme.palette.background.paper,
		border: "2px solid #000",
		boxShadow: theme.shadows[5],
		padding: theme.spacing(2, 4, 3),
	},
}));

function App() {
	const [posts, setPosts] = useState([]);
	const [open, setOpen] = useState(false); // Check open modal
	const classes = useStyles();
	const [modalStyle] = React.useState(getModalStyle);

	//useEffect -> Runs a piece of code based on a specific condition.
	useEffect(() => {
		// this is where the code runs
		getData();
	}, []);

	const getData = async () => {
		const postsCol = collection(db, "posts");
		const snapshot = await getDocs(postsCol);
		setPosts(
			snapshot.docs.map((doc) => ({
				id: doc.id,
				post: doc.data(),
			}))
		);
	};

	const handleClickSignUp = (childData) => {
		setOpen(childData);
	}

	return (
		<div className="App">
			{/* Header */}
			<Header takeMessSignUp={handleClickSignUp} />
			{/* Posts */}
			<div className="Post__list">
				{posts.map(({ id, post }) => (
					<PostItem key={id} data={post} />
				))}
			</div>
			<Modal open={open} onClose={() => setOpen(false)}>
				<div style={modalStyle} className={classes.paper}>
					<h2 id="simple-modal-title">Text in a modal</h2>
				</div>
			</Modal>
		</div>
	);
}

export default App;

Giải thích tý nè 😁

  • getModalStyle(): Function này để ta style cho modal phần vị trí hiển thi nó trên màn hình.
  • useStyles: Chứa đống style css cho modal của chúng ta.
  • handleClickSignUp(childData): Là một callback function để ta có thể hứng dữ liệu được truyền từ child component là Header ra parent component là App cho chúng ta biết là lúc nào có thể show modal.
  • takeMessSignUp={handleClickSignUp}: Truyền callback vào để hứng data từ component con thông qua attribute là takeMessSignUp

Trong Header component ta có code như sau:

import React, { useState } from "react";
import Button from "@material-ui/core/Button";
import "./Style-Header.css";

export default function Header(props) {
	const [openModal, setOpenModal] = useState(true);

	const transferMesageSignUp = () => {
		setOpenModal(true);
		props.takeMessSignUp(openModal);
	};

	return (
		<div className="container">
			<div className="header">
				<div className="header__logo">
					<a href="/#">
						<img
							src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
							alt="Logo"
						/>
					</a>
				</div>
				<div className="header__search">
					<input type="text" placeholder="Tìm kiếm" />
					<i className="bx bx-search-alt-2"></i>
				</div>
				<div className="header__login">
					<Button
						onClick={transferMesageSignUp}
						className="btn btn-login"
					>
						Sign up
					</Button>
				</div>
			</div>
		</div>
	);
}

Sau khi add được modal có sẵn từ Material-ui ta có thể custom nó thành form Sign Up và Sign In, tất nhiên bạn có thể tùy ý custom theo ý muốn cũng được nhé 😁.

Đầu tiên ta cần tạo form Sign Up và handle form này để ta có thể đăng ký tài khoản cho app của chúng ta.

Sign up form

Đây là UI của form SignUp, bước tiếp theo ta sử dụng những method mà firebase cung cấp cho chúng ta để tiến hành xử lý việc đăng ký tài khoản thôi 😁.

Trong file firebaseConfig.js ta chỉnh sửa lại một chút như sau:

import firebase from 'firebase/compat';

const firebaseApp = firebase.initializeApp( {
    apiKey: "***************************************",
    authDomain: "***************************************",
    databaseURL: "***************************************",
    projectId: "***************************************",
    storageBucket: "***************************************",
    messagingSenderId: "************",
    appId: "***************************************",
    measurementId: "************"
});

const db = firebaseApp.firestore();
const auth = firebaseApp.auth();
const storage = firebaseApp.storage();

export { db, auth, storage };

Việc config lại như trên, giúp ta có thể sử dụng các style code với firebase version trước phiên bản 9.0 ngay trên bản mới này luôn. Nên các bạn chú ý chút về điều này nhé 😁.

Trong file App.js các bạn thêm đoạn code sau:

import { auth, db } from "./firebaseConfig";
//...
import { Button, Input } from '@material-ui/core';

//...

function App() {
    //...
	const [email, setEmail] = useState('');
	const [password, setPassword] = useState('');
	const [username, setUsername] = useState('');
	const [user, setUser] = useState(null);
	//...

	//useEffect -> Runs a piece of code based on a specific condition.
	useEffect(() => {
		// this is where the code runs
		getData();
	}, []);

	useEffect(() => {
		const unSubcribe = auth.onAuthStateChanged((authUser) => {
			if(authUser) {
				// user has logged in...
				setUser(authUser);
                setUsername(authUser.displayName);

				if(authUser.displayName) {
					//don't update username
				} else {
					// if we just create someone...
					return updateProfile(authUser, {
						displayName: username,
					});
				}
			} else {
				// user has logged out...
				setUser(null);
			}
		});
		return () => {
			// Perform some cleanup actions
			unSubcribe();
		}
	}, [user, username])

	const getData = async () => {
		const postsCol = collection(db, "posts");
		const snapshot = await getDocs(postsCol);
		setPosts(
			snapshot.docs.map((doc) => ({
				id: doc.id,
				post: doc.data(),
			}))
		);
	};

	const handleClickSignUp = (childData) => {
		setOpen(childData);
	}

	const handleSignUp = (event) => {
		event.preventDefault();
		auth.createUserWithEmailAndPassword(email, password).then((authUser) => {
			return authUser.user.updateProfile({
				displayName: username
			});
		}).catch((error) => alert(error.message));
		setOpenModal(false);
	}

	return (
		<div className="App">
			{/* Header */}
			<Header takeMessSignUp={handleClickSignUp} />
			{/* Posts */}
			<div className="Post__list">
				{posts.map(({ id, post }) => (
					<PostItem key={id} data={post} />
				))}
			</div>
			//Modal form sign up
			<Modal open={open} onClose={() => setOpen(false)}>
				<div style={modalStyle} className={classes.paper}>
					<form className="form__signup">
						<img className="form__logo"
							src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
							alt="Logo"
						/>
						<div className="form__group">
							<label>User name:</label>
							<Input className="form__field" placeholder="username" type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
						</div>
						<div className="form__group">
							<label>Email:</label>
							<Input className="form__field" placeholder="email" type="text" value={email} onChange={(e) => setEmail(e.target.value)} />
						</div>
						<div className="form__group">
							<label>Password:</label>
							<Input className="form__field" placeholder="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
						</div>
						<Button className="btn-signup" onClick={handleSignUp}>Sign up</Button>
					</form>
				</div>
			</Modal>
		</div>
	);
}

export default App;

function handleSignUp() dùng để bắt sự kiện khi ta nhấn button Sign Up trong form Sign up thì gọi function createUserWithEmailAndPassword  firebase sẽ lưu lại những thông tin đó vào trong database sau đó ta có thể sử dụng tài khoản đó để Sign In thôi 😁.

Khi Sign In xong thì mình update displayName để ta có thể hiển thị khi ta comment dạo để chủ bài post biết 😁. Một cái hay của firebase là khi ta Sign Up thành công nó sẽ tự động Sign In luôn cho ta, để có thể Log Out thì trong file App.js ta thêm đoạn code như sau:

const handleClickLogOut = (childData) => {
		if(childData === true) {
			auth.signOut();
		}
	}

{/* Header */}
<Header takeMessSignUp={handleClickSignUp} takeMessLogOut={handleClickLogOut}  user={user}/>
  • user={user}: truyền data user cho component header để check khi nào SignUp hoặc Sign In thành công, từ đó ta có thể thay đổi button Sign Up, Sign In và Log Out.
  • takeMessLogOut={handleClickLogOut}: Truyền callback vào để lấy data mà header truyền ra cho app biết khi nào ta nhấn vào button Log Out để ta thực hiện đăng xuất khỏi app.
  • handleClickLogOut(): Nhiệm vụ của nó là check xem data từ header truyền ra nếu là true thì thực hiện Log Out khỏi app.

Đây là khi chưa có Sign Up hay Sign In:

Khi đã Sign In hoặc Sign Up thành công:

Ok, giời ta đã có thể đăng ký tài khoản được rồi 😉. Tiếp theo ta dùng tài khoản đã đăng ký và sign in vào app, để Sign In được ta tạo thêm một modal để nhập email và password vào.

Form Sign In chúng ta không cần field User name, do đó chúng ta chỉ việc copy cái modal của phần Sign Up và bỏ field User name đi là được 😁.

Modal Sign In:

<Modal open={openSignInModal} onClose={() => setOpenSignInModal(false)}>
				<div style={modalStyle} className={classes.paper}>
					<form className="form__signup">
						<img className="form__logo"
							src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
							alt="Logo"
						/>
						<div className="form__group">
							<label>Email:</label>
							<Input className="form__field" placeholder="email" type="text" value={email} onChange={(e) => setEmail(e.target.value)} />
						</div>
						<div className="form__group">
							<label>Password:</label>
							<Input className="form__field" placeholder="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
						</div>
						<Button className="btn-signup" onClick={handleSignIn}>Sign In</Button>
					</form>
				</div>
			</Modal>

Tiếp theo chúng ta handle việc Sign In, các bạn thêm đoạn code này vào trong App.js của mình:

//State
const [openSignInModal, setOpenSignInModal] = useState(false)

/* Header: takeMessLogIn={handleClickSignIn} */
<Header takeMessSignUp={handleClickSignUp} takeMessLogOut={handleClickLogOut} takeMessLogIn={handleClickSignIn} user={user}/>
    
const handleClickSignIn = (childData) => {
		setOpenSignInModal(childData);
	}

const handleSignIn = (event) => {
		event.preventDefault();
auth.signInWithEmailAndPassword(email, password).then(authUser => {
			return setUsername(authUser.user.displayName);
		}).catch((error) =>
			alert(error.message)
		);
	}

signInWithEmailAndPassword(): là phương thức mà firebase hổ trợ cho chúng ta có thể Sign In bằng email và password mà ta đã đăng ký.

Và đây là thành quả 😁.

Trước khi Sign In
Nhập email và password
Sign In thành công

Toàn bộ code trong file App.js:

import React, { useState, useEffect } from "react";
import "./App.css";
import Header from "./components/Header/Header";
import PostItem from "./components/PostItem/PostItem";
import { auth, db } from "./firebaseConfig";
import { collection, getDocs } from "firebase/firestore";
import { makeStyles } from "@material-ui/core/styles";
import Modal from "@material-ui/core/Modal";
import { Button, Input } from '@material-ui/core';


function getModalStyle() {
	const top = 50;
	const left = 50;

	return {
		top: `${top}%`,
		left: `${left}%`,
		transform: `translate(-${top}%, -${left}%)`,
	};
}

const useStyles = makeStyles((theme) => ({
	paper: {
		position: "absolute",
		width: 400,
		backgroundColor: theme.palette.background.paper,
		border: "2px solid #000",
		boxShadow: theme.shadows[5],
		padding: theme.spacing(2, 4, 3),
	},
}));

function App() {
	const [posts, setPosts] = useState([]);
	const [openModal, setOpenModal] = useState(false); // Check open modal
	const [openSignInModal, setOpenSignInModal] = useState(false)
	const [email, setEmail] = useState('');
	const [password, setPassword] = useState('');
	const [username, setUsername] = useState('');
	const [user, setUser] = useState(null);
	const classes = useStyles();
	const [modalStyle] = React.useState(getModalStyle);

	//useEffect -> Runs a piece of code based on a specific condition.
	useEffect(() => {
		// this is where the code runs
		getData();
	}, []);

	useEffect(() => {
		const unSubcribe = auth.onAuthStateChanged((authUser) => {
			if(authUser) {
				// user has logged in...
				setUser(authUser);
                setUsername(authUser.displayName);
			} else {
				// user has logged out...
				setUser(null);
			}
		});
		return () => {
			// Perform some cleanup actions
			unSubcribe();
		}
	}, [user, username])

	const getData = async () => {
		const postsCol = collection(db, "posts");
		const snapshot = await getDocs(postsCol);
		setPosts(
			snapshot.docs.map((doc) => ({
				id: doc.id,
				post: doc.data(),
			}))
		);
	};

	const handleClickSignUp = (childData) => {
		setOpenModal(childData);
	}

	const handleClickSignIn = (childData) => {
		setOpenSignInModal(childData);
	}

	const handleSignUp = (event) => {
		event.preventDefault();
		auth.createUserWithEmailAndPassword(email, password).then((authUser) => {
			return authUser.user.updateProfile({
				displayName: username
			});
		}).catch((error) => alert(error.message));
		setOpenModal(false);
	}

	const handleSignIn = (event) => {
        event.preventDefault();
auth.signInWithEmailAndPassword(email, password).then(authUser => {
			return setUsername(authUser.user.displayName);
		}).catch((error) =>
			alert(error.message)
		);
		setOpenSignInModal(false);
	}

	const handleClickLogOut = (childData) => {
		if(childData === true) {
			auth.signOut();
		}
	}

	return (
		<div className="App">
			{/* Header */}
			<Header takeMessSignUp={handleClickSignUp} takeMessLogOut={handleClickLogOut} takeMessLogIn={handleClickSignIn} user={user}/>
			{/* Posts */}
			<div className="Post__list">
				{posts.map(({ id, post }) => (
					<PostItem key={id} data={post} />
				))}
			</div>
			{/* Modal sign up */}
			<Modal open={openModal} onClose={() => setOpenModal(false)}>
				<div style={modalStyle} className={classes.paper}>
					<form className="form__signup">
						<img className="form__logo"
							src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
							alt="Logo"
						/>
						<div className="form__group">
							<label>User name:</label>
							<Input className="form__field" placeholder="username" type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
						</div>
						<div className="form__group">
							<label>Email:</label>
							<Input className="form__field" placeholder="email" type="text" value={email} onChange={(e) => setEmail(e.target.value)} />
						</div>
						<div className="form__group">
							<label>Password:</label>
							<Input className="form__field" placeholder="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
						</div>
						<Button className="btn-signup" onClick={handleSignUp}>Sign up</Button>
					</form>
				</div>
			</Modal>
			{/* Modal sign in */}
			<Modal open={openSignInModal} onClose={() => setOpenSignInModal(false)}>
				<div style={modalStyle} className={classes.paper}>
					<form className="form__signup">
						<img className="form__logo"
							src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
							alt="Logo"
						/>
						<div className="form__group">
							<label>Email:</label>
							<Input className="form__field" placeholder="email" type="text" value={email} onChange={(e) => setEmail(e.target.value)} />
						</div>
						<div className="form__group">
							<label>Password:</label>
							<Input className="form__field" placeholder="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
						</div>
						<Button className="btn-signup" onClick={handleSignIn}>Sign In</Button>
					</form>
				</div>
			</Modal>
		</div>
	);
}

export default App;
App.js

Toàn bộ code trong file Header.jsx:

import React, { useState } from "react";
import Button from "@material-ui/core/Button";
import "./Style-Header.css";

export default function Header(props) {
	const [openModal, setOpenModal] = useState(true);
	const [logOut, setLogOut] = useState(false);
	const [logIn, setLogIn] = useState(false);

	const transferMesageSignUp = () => {
		setOpenModal(true);
		props.takeMessSignUp(openModal);
	};

	const transferMessageLogOut = () => {
		setLogOut(true);
		props.takeMessLogOut(logOut)
	}

	const transferMessageLogIn = () => {
		setLogIn(true);
		props.takeMessLogIn(logIn);
	}

	return (
		<div className="container">
			<div className="header">
				<div className="header__logo">
					<a href="/#">
						<img
							src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
							alt="Logo"
						/>
					</a>
				</div>
				<div className="header__search">
					<input type="text" placeholder="Tìm kiếm" />
					<i className="bx bx-search-alt-2"></i>
				</div>
				<div className="header__login">
					{props.user ? (
						<Button
						onClick={transferMessageLogOut}
						className="btn btn-login"
						>
							Log out
						</Button>
					) : (
						<div>
							<Button
							onClick={transferMessageLogIn}
							className="btn btn-login"
							>
								Sign in
							</Button>
							<Button
							onClick={transferMesageSignUp}
							className="btn btn-sign-up"
							>
								Sign up
							</Button>
						</div>
					)}
					
				</div>
			</div>
		</div>
	);
}
header.jsx

III. Tổng kết.

Ok, bài viết đến đây mình nghĩ là đã đủ. Chúng ta đã hoàn thành xong việc tạo form Sign Up, Sign In, sử dụng được các phương thức mà firebase cung cấp cho việc authentication và nhúng nó vào trong app của mình. Phần tiếp theo chúng ta sẽ tiến hành lưu những bài post mới kèm theo ảnh vào database nhé 😉. Hẹn gặp các bạn trong phần tiếp theo 😁. Cảm ơn các bạn đã đọc 🤗

Bài viết cùng seri

Bài viết liên quan

Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 5

Trong phần 5 này, chúng ta sẽ hoàn thiện Guest Home Page và config để connect với firebase, chuẩn bị cho các phần tiếp theo nhé 😉....

Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 5
Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 4

Trong phần 4 của series này, chúng ta sẽ tiếp tục hoàn thiện phần Guest Home Page . Lần này, chúng ta sẽ bắt tay vào code phần FAQs component, thực hiện config router để redirects qua lại các page nhé. Cùng bắt đầu thôi 😉....

Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 4
Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 3

Tiếp tục công việc đang dang dở ở phần 2, trong phần 3 này chúng ta sẽ làm phần Footer ở Guest Home Page này, kèm theo đó chúng ta sẽ apply các tool như ESLint và Prettier vào project của chúng ta nhé....

Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 3
Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 2

Sau phần 1 của series này, chắc hẵn các bạn đã nóng lòng muốn bắt tay ngay vào code rồi đúng không 😁. Okie, trong phần thứ 2 của series này, chúng ta sẽ cùng nhau tạo phần Story của trang Home dành cho khách trên trang Netflix nhé (Trang lúc ta vào mà chưa login ấy 😁). Nào! bắt đâu thôi!...

Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 2
Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 1

Hello các bạn, hôm nay mình sẽ mang đến cho các bạn một series mới về chuyên mục ReactJS đó là series "Netflix Clone with ReactJS, Styled Components and Firebase (Firestore & Auth)". Trong phần đầu tiên của series này, chúng ta sẽ setup project của chúng ta trước nhé. Nào bắt đầu thôi 😉....

Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 1
You've successfully subscribed to 200Lab Blog
Great! Next, complete checkout for full access to 200Lab Blog
Xin chào mừng bạn đã quay trở lại
OK! Tài khoản của bạn đã kích hoạt thành công.
Success! Your billing info is updated.
Billing info update failed.
Your link has expired.