---
title: "Netflix Clone với ReactJS, Styled Components và Firebase (Firestore & Auth) - Phần 2"
description: "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!"
canonical: "https://200lab.io/blog/netflix-clone-voi-reactjs-styled-components-va-firebase-firestore-auth-phan-2"
published: "2021-12-01T01:50:00Z"
updated: "2026-08-21T07:17:39Z"
authors: ["TỪ QUỐC HƯNG"]
tags: ["Javascript"]
reading_minutes: 9
access: "public"
---

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!

## I. Tạo file data tĩnh.

Tạo file data tĩnh là sao? Những data tĩnh là những data như hình bên dưới, chúng ta sẽ không lưu trực tiếp trên file `.js` hay `.jsx` mà chúng ta lưu chúng dưới dạng **JSON **và load lên thôi.

![Static data in UI](https://assets.200lab.io/images/2024/12/05/image-14-8c5f01158761769aa1c0.webp)

Trong project của chúng ta, các bạn tạo thư mục **fixtures**, trong đó ta sẽ tạo 2 file data khác nhau, `faqs.json` cho phần **FAQs** và `stories.json` cho các **Story** của trang, như hình bên dưới.

![fixtures folder](https://assets.200lab.io/images/2024/12/05/image-15-34e4742e58b07ec6d569.webp)

Trong các file đã tạo ở trên, các bạn thêm data cho nó như sau:

- `faqs.json`

```JSON
[
	{
		"id": "dffee64b-bf69-4f7f-a056-e4e99718c2d7",
		"header": "What is Netflix?",
		"body": "Netflix is a streaming service that offers a wide variety of award-winning TV shows, movies, anime, documentaries, and more on thousands of internet-connected devices.\n\nYou can watch as much as you want, whenever you want without a single commercial – all for one low monthly price. There's always something new to discover and new TV shows and movies are added every week!"
	},
	{
		"id": "308e3980-d9b8-43f6-80a5-2a855bc7364e",
		"header": "How much does Netflix cost?",
		"body": "Watch Netflix on your smartphone, tablet, Smart TV, laptop, or streaming device, all for one fixed monthly fee. Plans range from 70,000 ₫ to 260,000 ₫ a month. No extra costs, no contracts."
	},
	{
		"id": "a8231110-c2a7-42a3-998a-ec936e8e500d",
		"header": "Where can I watch?",
		"body": "Watch anywhere, anytime. Sign in with your Netflix account to watch instantly on the web at netflix.com from your personal computer or on any internet-connected device that offers the Netflix app, including smart TVs, smartphones, tablets, streaming media players and game consoles.\n\nYou can also download your favorite shows with the iOS, Android, or Windows 10 app. Use downloads to watch while you're on the go and without an internet connection. Take Netflix with you anywhere."
	},
	{
		"id": "d40d6041-4573-4794-8c60-41afa8aaec3b",
		"header": "How do I cancel?",
		"body": "Netflix is flexible. There are no pesky contracts and no commitments. You can easily cancel your account online in two clicks. There are no cancellation fees – start or stop your account anytime."
	},
	{
		"id": "e32a5639-6149-474c-8dcc-3231acc18484",
		"header": "What can I watch on Netflix?",
		"body": "Netflix has an extensive library of feature films, documentaries, TV shows, anime, award-winning Netflix originals, and more. Watch as much as you want, anytime you want."
	},
	{
		"id": "ee556aad-708a-4f6e-8ba9-32921ee885d3",
		"header": "Is Netflix good for kids?",
		"body": "The Netflix Kids experience is included in your membership to give parents control while kids enjoy family-friendly TV shows and movies in their own space.\n\nKids profiles come with PIN-protected parental controls that let you restrict the maturity rating of content kids can watch and block specific titles you don’t want kids to see."
	}
]
```

_faqs.json_

- `stories.json`

```JSON
[
    {
        "id": "eeba294b-0629-4360-94a4-c085ef78b33d",
        "title": "Enjoy on your TV.",
        "subTitle": "Watch on Smart TVs, Playstation, Xbox, Chromecast, Apple TV, Blu-ray players, and more.",
        "image": "/images/misc/home-tv.png",
        "alt": "Tiger King on Netflix",
        "direction": "row"
    },
    {
        "id": "071ddd8a-2ba3-4e7d-bf48-b1c1c2c0013a",
        "title": "Download your shows to watch offline.",
        "subTitle": "Save your favorites easily and always have something to watch.",
        "image": "/images/misc/home-mobile.png",
        "alt": "Watch on mobile",
        "direction": "row-reverse"
    },
    {
        "id": "b5b21681-662a-44b8-83e6-63c7e3cf567a",
        "title": "Watch everywhere.",
        "subTitle": "Stream unlimited movies and TV shows on your phone, tablet, laptop, and TV.",
        "alt": "Money Heist on Netflix",
        "direction": "row"
    },
    {
        "id": "d3fdfefb-3d43-4422-84b7-257a72a624b3",
        "title": "Create profiles for kids.",
        "subTitle": "Send kids on adventures with their favorite characters in a space made just for them—free with your membership.",
        "image": "/images/misc/for-kids.png",
        "alt": "Netflix for kids",
        "direction": "row-reverse"
    }
]
```

_stories.json_

> _**Note:** để có được id như mình thì các bạn lên trang Online GUID / UUID Generator __tại đây__ để có thể lấy các id tự generate ra nhé._

Tiếp theo các bạn tạo thư mục **images/misc** để chứa hình ảnh cho phần Story **Guest Home Page** của chúng ta. Hình ảnh các bạn có thể download [tại đây](<https://drive.google.com/drive/folders/1yr3N7EIRsk2GqridcqFOlAR6LKvaIuiy?usp=sharing>).

Bạn cũng có thể đặt tên thư mục tùy ý nhưng phải đảm bảo là sau đó bạn phải edit lại phần data JSON của file `stories.json` cho đúng **path** để ảnh có thể load được nhé 😉.

## II. Tạo component Story.

Component **Story** là sao? Thì component **Story** là những component nó view những content như hình bên dưới.

![Story component](https://assets.200lab.io/images/2024/12/05/image-16-b2052140f591f7da13f7.webp)

Đầu tiên ta cần tạo folder **components**, trong folder **components** ta tạo folder và file như sau:

![Story component folder](https://assets.200lab.io/images/2024/12/05/image-17-a5d1224206aa38e59dc4.webp)

**_Note_**:

- **story.jsx**: file chứa các **child component** được define để build nên component story.
- **index.jsx**: nó là một **compound component**, nó dùng để chứa các component được định nghĩa ở file `story.jsx` và sử dụng nó để build nên component story đồng thời quản lý các `state` hoặc `props` được truyền vào.

Khái quát một chút về **compound component**:

> _**Compound components** là 1 design pattern trong React, nó là loại component có thể quản lí `internal state` của nó và component đó được render như thế nào thì đều nằm ở phía **implementation** chứ không phải _ở _**declaration** ._

Okie quay trở lại, trong folder **src** ta tạo file `global-styles.jsx` để css global cho cả trang của chúng ta, ta thêm code vào như bên dưới.

```JS
import { createGlobalStyle } from 'styled-components';

export const GlobalStyles = createGlobalStyle`
    html, body {
        font-family: Arial, Helvetica, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        background-color: #000;
        color: #333;
        font-size: 16px;
    }
`;
```

_global-styles.jsx_

Sau đó sử dụng component này ở trong `/src/index.js` như bên dưới:

```JS
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { GlobalStyles } from "./global-styles";

ReactDOM.render(
	<React.StrictMode>
		<GlobalStyles />
    	<App />
  </React.StrictMode>,
  document.getElementById("root")
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
```

_index.js_

Trong file `story.jsx` ta define các **child component** như sau:

```JS
import styled from "styled-components/macro";

export const Inner = styled.div`
	display: flex;
	align-items: center;
	flex-direction: ${({ direction }) => direction};
	justify-content: space-between;
	max-width: 1100px;
	margin: auto;
	width: 100%;

	@media screen and (max-width: 1000px) {
		flex-direction: column;
	}
`;

export const BlockPane = styled.div`
	width: 50%;

	@media screen and (max-width: 1000px){
		width: 100%;
		padding: 0 45px;
		text-align: center;
	}
`;

export const Title = styled.h1`
	font-size: 50px;
	line-height: 1.1;
	margin-bottom: 8px;

	@media screen and (max-width: 600px){
		font-size: 35px;
	}
`;

export const SubTitle = styled.h2`
	font-size: 26px;
	line-height: normal;
	font-weight: normal;

	@media screen and (max-width: 600px){
		font-size: 18px;
	}
`;

export const Image = styled.img`
	max-width: 100%;
	width: 507px;
	height: auto;
`;

export const Item = styled.div`
	display: flex;
	border-bottom: 8px solid #222;
	padding: 50px 5%;
	color: #fff;
	overflow: hidden;
`;

export const Container = styled.div`
	@media (max-width: 1000px) {
		${Item}:last-of-type h2 {
			margin-bottom: 50px;
		}
	}
`;
```

_story.jsx_

Tiếp theo đến với file `/story/index.jsx`, vì nó là một **compound component**, nên tại đây ta có thể quản lý và sử dụng các props được **binding **vào như sau:

```JS
import React from "react";
import { BlockPane, Container, Image, Inner, Item, SubTitle, Title } from "./styles/story";

export default function Story({ children, direction = 'row', ...restProps }) {
    return (
        <Item {...restProps}>
            <Inner direction={direction}>{children}</Inner>
        </Item>
    );
}

Story.Container = function StoryContainer({ children, ...restProps }) {
    return <Container {...restProps}>{children}</Container>
}

Story.BlockPane = function StoryBlockPane({ children, ...restProps }) {
    return <BlockPane {...restProps}>{children}</BlockPane>
}

Story.Title = function StoryTitle({ children, ...restProps }) {
    return <Title {...restProps}>{children}</Title>
}

Story.SubTitle = function StorySubTitle({ children, ...restProps }) {
    return <SubTitle {...restProps}>{children}</SubTitle>
}

Story.Image = function StoryImage({ children, ...restProps }) {
    return <Image {...restProps}>{children}</Image>
}

Story.SubTitle = function StorySubTitle({ children, ...restProps }) {
    return <SubTitle {...restProps}>{children}</SubTitle>
}
```

_index.jsx_

**_Note:_**

- **children**: prop chứa các content của component,
- **direction**: prop này dùng để nhận các value css của component để sử dụng trong thằng `<Inner />` của file `story.jsx`,
- **restProps**: là một **rest parameter**.

Okie! define như vậy là đủ rồi hehe 😁, Vậy sử dụng nó như thế nào? Thì để sử dụng những thứ mà chúng ta define nảy giờ như sau:

- Trong file `App.js`

```JS
import React from 'react';
import Story from './components/story/index';
import storiesData from './fixtures/stories.json'

export default function App() {
	return (
		<Story.Container>
			{storiesData.map(story => (
				<Story key={story.id} direction={story.direction}>
					<Story.BlockPane>
						<Story.Title>{story.title}</Story.Title>
						<Story.SubTitle>{story.subTitle}</Story.SubTitle>
					</Story.BlockPane>
					{story.image ? (
						<Story.BlockPane>
							<Story.Image src={story.image} alt={story.alt}></Story.Image>
						</Story.BlockPane>
					) : ''}
				</Story>
			))}
		</Story.Container>
	);
}
```

_App.js_

Đấy cũng đơn giản phết nhỉ 😁, các component về sau chúng ta cũng tạo theo cách này nhé 😉, run thử xem chạy như thế nào nhé.

![Story component in UI](https://assets.200lab.io/images/2024/12/05/image-18-70a49cdc68e4fad184d8.webp)

Okie! work tốt nhá hehe 😎.

## III. Tổng kết.

Okie! Phần thứ 2 của series này tới đây thôi, cơ bản chúng ta cũng làm được 50% **Guest Home Page** rồi. Đồng thời với ngần này thì đã đủ cho các bạn làm quen và code được theo style của **styled-component **rồi. Trong phần tiếp theo chúng ta sẽ làm phần **FAQs** của **Guest Home Page** này và install thêm các tool như **ESLint** và **Prettier** để có thể check convention của chúng ta khi coding giúp code của chúng ta gọn gàng, đẹp và bớt _"gà"_ hơn nhé 😁.
