Trong bài hướng dẫn này, chúng ta sẽ xây dựng một trò chơi đơn giản. Bạn có thể không muốn đọc tiếp vì bạn không làm game — Đừng làm vậy nhé! Hãy kiên nhẫn một chút. Kiến thức trong bài này chính là lý thuyết cơ bản để xây dựng một ứng dụng React, master nó sẽ giúp bạn hiểu sâu hơn về React.
Mẹo Hướng dẫn này được thiết kế cho những ai có xu hướng Học thông qua thực hành. Nếu bạn là người thích học từ những lý thuyết cơ bản, vui lòng tham khảo Hướng dẫn step-by-step. Bạn có thể tham khảo cả hai để bổ sung cho nhau.
Hướng dẫn này bao gồm 4 phần chính như sau:
- sẽ giúp bạn chọn ra điểm xuất phát phù hợp để bắt đầu.
- sẽ giới thiệu cho bạn các khái niệm cơ bản của React: components, props, và state.
- sẽ giúp bạn hiều được những kỹ thuật cơ bản nhất trong quá trình xây dựng một ứng dụng bằng React.
- sẽ cho bạn một cái nhìn sâu sắc hơn về những điểm mạnh độc đáo của React.
Bạn không cần phải hoàn thành toàn bộ các phần trên trong một lần để có được kiến thức mà nó mang lại. Tuy nhiên, hãy cố gắng hoàn thành càng nhiều phần càng tốt — kể cả là một hay hai phần
Chúng ta sẽ làm gì?
Trong bài hướng dẫn này, Chúng ta sẽ xây dựng một trò chơi tương tác có tên là tic-tac-toe bằng React.
Bạn có thể tìm thấy phiên bản hoàn thiện mà chúng ta sẽ xây dựng tại đây. Nếu bạn không hiểu cú pháp hoặc ý nghĩa của đoạn code đó, đừng lo lắng! Mục tiêu của bài hướng dẫn này là giúp bạn hiểu được React cũng như cú pháp trong React
Chúng tôi khuyến khích bạn tìm hiểu trò tic-tac-toe trước khi tiếp tục. Một trong những tính năng của trò chơi mà bạn cần chú ý đó là danh sách bên phải của bàn cờ. Danh sách đó chính là lịch sử của tất cả các bước đi trong một ván chơi.
Nếu bạn đã làm quen với trò chơi tic-tac-toe, chúng ta sẽ bắt đầu với một phiên bản đơn giản của nó trong bài hướng dẫn này. Bước tiếp theo sẽ giúp bạn cài đặt những thứ cần thiết để bạn có thể bắt đầu.
Kiến thức cơ bản cần có
Chúng tối giả định rằng bạn đã có kiến thức cơ bản về HTML và JavaScript, tuy nhiên, bạn vẫn có thể tiếp tục nếu như bạn đã làm quen với một ngôn ngữ lập trình khác. Chúng tôi cũng giả định rằng bạn đã quen thuộc với những khái niệm hàm [functions], đối tượng [objects], mảng [arrays] và một chút khái niệm về kế thừa [extend] và lớp [classes].
Nếu bạn cần tìm hiểu về JavaScript, chúng tôi khuyến khích bạn đọc hướng dẫn này. Chú ý rằng trong bài viết này có sử dụng một vài tính năng của ES6 — một phiên bản gần đây của JavaScript. Trong bài hướng dẫn này có sử dụng các khái niệm arrow functions, classes,
return React.createElement['div', {className: 'shopping-list'},
React.createElement['h1', /* ... h1 children ... */],
React.createElement['ul', /* ... ul children ... */]
];
4, và
return React.createElement['div', {className: 'shopping-list'},
React.createElement['h1', /* ... h1 children ... */],
React.createElement['ul', /* ... ul children ... */]
];
5. Bạn có thể sử dụng để kiểm tra xem cú pháp ES6 được biên dịch thành gì tương ứng.
Cài đặt môi trường
Có hai cách để hoàn thành bài hướng dẫn này: Viết code trực tiếp trên trình duyệt hoặc cài đặt môi trường trên máy cá nhân của bạn
Tuỳ chọn 1: Viết code trên trình duyệt
Đây là cách nhanh nhất để bắt đầu!
Đầu tiên, mở đoạn Code khởi đầu này trong trang mới. Bạn sẽ thấy một bàn cờ tic-tac-toe trống và code React tương ứng. Chúng ta sẽ sửa đoạn code đó trong bài hướng dẫn này.
Bạn có thể bỏ qua tùy chọn cài đặt thứ hai và tiếp tục chuyển đến phần để có cái nhìn tổng quan về React.
Tùy chọn 2: Môi trường trên máy cá nhân
Đây là tùy chọn không bắt buộc cho bài hướng dẫn này!
Tùy chọn: Hướng dẫn cài đặt trên máy cá nhân với trình soạn thảo văn bản tùy thích
Các bước cài đặt dưới đây sẽ mất nhiều thời gian hơn nhưng nó cho phép bạn hoàn thành bài hướng dẫn này bằng trình soạn thỏa ưa thích của mình. Các bước cần làm:
- Cài đặt Node.js.
- Làm theo để tạo một project mới.
npx create-react-app my-app
- Xóa toàn bộ tệp tin trong thư mực
return React.createElement['div', {className: 'shopping-list'}, React.createElement['h1', / ... h1 children ... /], React.createElement['ul', / ... ul children ... /]
];
6
Chú ý:
Đừng xóa thư mục
return React.createElement['div', {className: 'shopping-list'},
React.createElement['h1', / ... h1 children ... /],
React.createElement['ul', / ... ul children ... /]];
7, chỉ xóa các tệp tin bên trong nó. Chúng ta sẽ thay thế các tệp tin đó bằng các tệp tin mẫu trong bước tiếp theocd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
- Tạo một tệp tin với tên
return React.createElement['div', {className: 'shopping-list'}, React.createElement['h1', / ... h1 children ... /], React.createElement['ul', / ... ul children ... /]
];
8 trong thư mục
6 với đoạn code CSS này.return React.createElement['div', {className: 'shopping-list'}, React.createElement['h1', / ... h1 children ... /], React.createElement['ul', / ... ul children ... /]
];
- Tạo một tệp tin với tên
class Board extends React.Component { renderSquare[i] {
}return ; }
0 trong thư mục
6 với đoạn code JS này.return React.createElement['div', {className: 'shopping-list'}, React.createElement['h1', / ... h1 children ... /], React.createElement['ul', / ... ul children ... /]
];
- Thêm 3 dòng code sau vào đầu tệp
class Board extends React.Component { renderSquare[i] {
}return ; }
0 trong thư mục
6:return React.createElement['div', {className: 'shopping-list'}, React.createElement['h1', / ... h1 children ... /], React.createElement['ul', / ... ul children ... /]
];
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
Tiếp theo, chạy lệnh
class Board extends React.Component {
renderSquare[i] {
return ; }
}
4 trong thư mục project và mở đường dẫn
class Board extends React.Component {
renderSquare[i] {
return ; }
}
5 trên trình duyệt, bạn sẽ nhìn thấy bàn cờ tic-tac-toe trống.
Chúng tôi khuyến khích bạn làm theo hướng dẫn này để cấu hình việc làm nổi bật cú pháp cho trình soạn thảo của bạn.
Cứu, Tôi đang bị kẹt!
Nếu bạn bị kẹt tại một bước nào đó, hãy ghé thăm cộng đồng hỗ trợ. Trong tình uống cụ thể, Kênh chat Reactiflux là một cách hiệu quả để giúp bạn vượt qua khó khăn. Nếu bạn không nhận được câu trả lời từ ai hoặc bạn vẫn gặp khó khăn, vui lòng gửi vấn đề đó cho chúng tôi và chúng tôi sẽ giúp bạn.
Tổng quan
Mọi thứ đã sẵn sàng, cùng tìm hiểu tổng quan về React nào!
React là gì?
React là một thư viện JavaScript declarative, hiệu quả và linh hoạt cho việc xây dựng giao diện người dùng. React cho phép bạn tạo những giao diện [UI] phức tạp từ những đoạn code nhỏ và độc lập. Những đoạn code này được gọi là “components”.
React có một vài loại components, tuy nhiên chúng ta sẽ bắt đầu với
class Board extends React.Component {
renderSquare[i] {
return ; }
}
6:
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
Chúng ta sẽ sớm tìm hiểu sự thú vị của thẻ XML sau. Chúng ta sử dụng components để nói cho React biết ta muốn hiển thị gì trên màn hình. Khi dữ liệu thay đổi, React sẽ cập nhật và render lại các component một cách hiệu quả.
Ở ví dụ trên, ShoppingList là một React component class, hoặc React component type. Một component nhận vào các tham số, được gọi là
class Board extends React.Component {
renderSquare[i] {
return ; }
}
7 [viết tắt của “properties”], và trả về một hệ thống cấp bậc các view để hiển thị thông qua phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8.
Phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 trả về mô tả của những gì bạn muốn nhìn thấy trên màn hình. React nhận mô tả và hiển thị ra kết quả. Cụ thể, phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 trả về một React element, một bản mô tả gọn nhẹ của những gì được render. Hầu hết lập trình viên React sử dụng một cú pháp đặc biệt được gọi là “JSX” để viết những cấu trúc này dễ dàng hơn. Khi biên dịch, thẻ
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
1 sẽ được chuyển thành
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
2. Ví dụ trên sẽ tương đương với:
return React.createElement['div', {className: 'shopping-list'},
React.createElement['h1', /* ... h1 children ... */],
React.createElement['ul', /* ... ul children ... */]
];
Nếu bạn hiếu kỳ,
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
3 được mô tả chi tiết hơn tại , tuy nhiên, chúng ta không sử dụng nó trong bài hướng dẫn này. Thay vào đó, chúng ta sẽ sử dụng cú pháp JSX.
JSX đi kèm với toàn bộ sức mạnh của JavaScript. Trong JSX, bạn có thể viết bất kỳ biểu thức JavaScript nào trong dấu ngoặc. Mỗi React element là một đối tượng JavaScript, bạn có thể lưu trữ hoặc truyền qua lại trong chương trình của bạn.
Component
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
4 ở trên chỉ render những DOM component có sẵn như
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
1 và
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
6. Tuy nhiên bạn cũng có thể tạo ra và render những React component tùy biến. Ví dụ, chúng ta có thể chỉ định đến danh sách các cửa hàng bằng cách viết
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
7. Mỗi React component được đóng gói và có thể hoạt động độc lập; việc này cho phép bạn xây dựng những UI phức tạp từ các component đơn giản.
Xem xét đoạn code khởi đầu
Nếu bạn code trên trình duyệt, mở đường dẫn này trong trang mới: Code khởi đầu. Nếu bạn code trên máy cá nhân, mở tệp
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
8 trong thư mục project [bạn đã tạo tệp này trong mục ]
Đoạn code khởi đầu này là cơ sở cho những gì chúng ta sẽ xây dựng. Chúng tôi đã cung cấp sẵn CSS, vì vậy bạn chỉ cần tập trung vào việc sử dụng React để tạo ra trò chơi tic-tac-toe.
Bằng việc xem xét đoạn code, bạn sẽ thấy đang có ba React component:
- Square
- Board
- Game
Square component sẽ render một
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
9 và Board sẽ render 9 ô vuông [9 Square component]. Game component render một bàn cờ [Board component] với các ô giá trị sẽ được sửa đổi ngay sau đây. Hiện tại bạn chưa thể tương tác với component nào.
Truyền dữ liệu thông qua Props
Để hiểu phần này, hãy thử truyền một vài dữ liệu từ Board component xuống Square component.
Chúng tôi đặc biệt khuyến nghị bạn gõ code trực tiếp thay vì việc copy/paste code từ bài hướng dẫn. Điều này sẽ giúp bạn ghi nhớ và hiểu sâu sắc hơn.
Trong phương thức
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
0 của Board component, thay đổi code để truyền props
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
1 xuống Square component:
class Board extends React.Component {
renderSquare[i] {
return ; }
}
Thay đổi phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Square component để hiển thị giá trị được truyền vào bằng cách thay
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
3 bằng
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
4:
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
Trước:
Sau: Bạn sẽ nhìn thấy số thứ tự tương ứng trên mỗi ô vuông trong kết quả được render ra
Xem code chi tiết tại bước này
Chúc mừng! Bạn vừa “truyền một prop” từ component cha xuống component con [từ Board xuống Square]. Truyền props là cách truyền thông tin trong một ứng dụng React, từ cha xuống con.
Tạo ra một component có thể tương tác được
Hãy điền giá trị “X” vào Square component khi ta bấm vào nó. Đầu tiên, thay đổi thẻ button trong hàm
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
5 của Square component như sau:
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
Nếu bạn bấm vào một ô Square, bạn sẽ nhìn thấy một cảnh báo hiện ra trên trình duyệt
Chú ýĐể tránh xảy ra lỗi mập mờ của từ khóa
class Square extends React.Component { render[] { return [ {this.props.value} ]; } }
6, Chúng ta sẽ sử dụng cú pháp arrow function cho việc xử lý sự kiện như sau:
class Square extends React.Component { render[] { return [ console.log['click']}> {this.props.value} ]; } }
Để ý thấy rằng bằng cách viết
class Square extends React.Component { render[] { return [ {this.props.value} ]; } }
7, ta đã truyền một hàm [function] thông qua prop
class Square extends React.Component { render[] { return [ {this.props.value} ]; } }
8. React sẽ chỉ thực hiện hàm này khi Square component được bấm. Quên không viết
class Square extends React.Component { render[] { return [ {this.props.value} ]; } }
9 mà chỉ viết
0 là một lỗi cơ bản trong react, khi viết như thế, cảnh báo [alert] sẽ được hiện mỗi khi component render lại.
class Square extends React.Component { render[] { return [ console.log['click']}> {this.props.value} ]; } }
Ở bước tiếp theo, chúng ta muốn Square component “ghi nhớ” rằng nó đã được bấm, và hiện giá trị “X” trên nó. Để “ghi nhớ” mọi thứ, các component sử dụng state.
Các React component có thể có state bằng cách thiết lập
class Square extends React.Component {
render[] {
return [
console.log['click']}> {this.props.value}
];
}
}
1 trong hàm khởi tạo của nó.
class Square extends React.Component {
render[] {
return [
console.log['click']}> {this.props.value}
];
}
}
2 nên được cân nhắc là riêng tư đối với mỗi React component mà nó được định nghĩa. Bây giờ, hãy lưu giá trị hiện tại của Square trong
class Square extends React.Component {
render[] {
return [
console.log['click']}> {this.props.value}
];
}
}
1 và thay đổi nó khi Square được bấm.
Đầu tiên, hãy thêm hàm khởi tạo để khởi tạo giá trị của state:
class Square extends React.Component {
constructor[props] { super[props]; this.state = { value: null, }; }
render[] {
return [
console.log['click']}>
{this.props.value}
];
}
}
Chú ýTrong JavaScript classes, bạn luôn luôn cần gọi hàm
class Square extends React.Component { render[] { return [ console.log['click']}> {this.props.value} ]; } }
4 khi định nghĩa hàm khởi tạo của một lớp con [subclass]. Tất cả các React component dạng class nếu có hàm khởi tạo
class Square extends React.Component { render[] { return [ console.log['click']}> {this.props.value} ]; } }
5 nên bắt đầu với việc gọi hàm
6.
class Square extends React.Component { render[] { return [ console.log['click']}> {this.props.value} ]; } }
Bây giờ, chúng ta sẽ thay đổi phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của component Square để hiển thị giá trị state khi nó được bấm:
- Thay thế
class Square extends React.Component { render[] { return [
]; } }console.log['click']}> {this.props.value}
8 bằng
class Square extends React.Component { render[] { return [
]; } }console.log['click']}> {this.props.value}
9 trong thẻ
9.class Square extends React.Component { render[] {
} }return [ {this.props.value} ];
- Thay thế hàm xử lý sự kiện
class Square extends React.Component { constructor[props] { super[props]; this.state = { value: null, }; } render[] {
} }return [ console.log['click']}> {this.props.value} ];
1 bằng
2.class Square extends React.Component { constructor[props] { super[props]; this.state = { value: null, }; } render[] {
} }return [ console.log['click']}> {this.props.value} ];
- Để 2 props
class Square extends React.Component { constructor[props] { super[props]; this.state = { value: null, }; } render[] {
} }return [ console.log['click']}> {this.props.value} ];
3 và
8 trên 2 dòng khác nhau để việc đọc code dễ dàng hơn.class Square extends React.Component { render[] {
} }return [ {this.props.value} ];
Sau những thay đổi trên, thẻ
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
9 trong phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Square component sẽ như sau:
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
0
Bằng việc gọi hàm
class Square extends React.Component {
constructor[props] { super[props]; this.state = { value: null, }; }
render[] {
return [
console.log['click']}>
{this.props.value}
];
}
}
7 từ hàm xử lý
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
8 trong phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Square component, chúng ta đã thông báo cho React render lại Square component khi thẻ
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
9 được bấm. Sau khi cập nhật, giá trị của
class Square extends React.Component {
render[] {
return [
console.log['click']}> {this.props.value}
];
}
}
9 trong Square component sẽ là
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
02, vì vậy, ta sẽ nhìn thấy
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
03 trên bàn cờ. Khi bạn bấm vào bất kỳ một ô Square nào, giá trị X sẽ được hiển thị trên nó.
Khi bạn gọi hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
04 trong một component, React sẽ tự động cập nhật chính component đó và các component con của nó.
Xem code chi tiết tại bước này
Công cụ cho nhà phát triển
Ứng dụng mở rộng React Devtools trên Chrome và Firefox cho phép bạn kiểm tra một cây React component với công cụ dành cho nhà phát triển trên trình duyệt.
React DevTools cho phép bạn kiểm tra props và state hiện tại của các React component.
Sau khi cài đặt React DevTools, bạn có thể nhấp chuột phải vào bất kì phần tử nào trên trang, chọn “Kiểm tra phần tử” [“Inspect”] để mở công cụ dành cho nhà phát triển, React tabs [“⚛️ Components” và “⚛️ Profiler”] sẽ xuất hiện ở tabs cuối cùng bên phải. Sử dụng “⚛️ Components” để kiểm tra cây component.
Tuy nhiên, cần thêm một vài bước để kiểm tra cây component nếu bạn code trên trình duyệt với CodePen:
- Đăng nhập hoặc đăng ký và xác thực email [bắt buộc để tránh spam].
- Bấm nút “Fork”.
- Bấm “Change View” và sau đó chọn “Debug mode”.
- Trong trang mới được mở ra, devtools sẽ hiển thị một tab React.
Hoàn thành trò chơi
Hiện tại, chúng ta đã có khối cơ bản để có thể tạo nên trò tic-tac-toe. Để có một trò chơi hoàn chỉnh, ta cần đặt xen kẽ “X” và “O” trên bàn cờ và xác định xem ai là người chiến thắng.
Tách state lên component cha
Hiện tại, mỗi Square component đang giữ state của chính nó. Và để biết ai là người thắng cuộc, ta cần biết giá trị của cả 9 ô vuông.
Đến đây, bạn có thể sẽ nghĩ để biết trạng thái của cả 9 ô thì Board component sẽ hỏi mỗi Square component xem giá trị state hiện tại của nó là gì. Mặc dù ta hoàn toàn có thể làm như vậy trong React, nhưng chúng tôi không khuyến khích làm như vậy bởi vì khi làm thế , sẽ rất khó để đọc hiểu code, debug cũng như là tái cấu trúc code. Hướng tiếp cận tốt nhất ở đây là hãy lưu state của cả 9 Square component [component con] ở Board component [component cha]. Board component sẽ truyền dữ liệu xuống Square component để nó biết phải hiện thị gì thông qua prop, .
Để thu thập dữ liệu từ nhiều component con hay để hai component con giao tiếp được với nhau, bạn cần khai báo state chung trong component cha của các component đó. Component cha có thể truyền state xuống các component con thông qua props; việc này giúp các component con đồng bộ với nhau và đồng bộ với component cha của nó.
Tách state lên component cha là khá phổ biến trong React khi các component được tái cấu trúc [refactor] — Hãy áp dụng trong trường hợp này nào.
Thêm vào Board component hàm khởi tạo constructor và khởi tạo giá trị state là một mảng 9 phần tử null tương ứng với 9 ô vuông:
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
1
Khi chúng ta tương tác [bấm vào các Square] trên bàn cờ, mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
05 sẽ có thể giống như thế này:
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
2
Phương thức
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
0 của Board component hiện tại:
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
3
Ở trên, chúng ta đã từ Board component để hiển thị các giá trị từ 0 đến 8 ở mỗi Square component. Ở một bước khác, chúng ta đã thay thế các số này bằng giá trị “X” để . Đây là lý do tại sao Square component hiện tại không sử dụng đến prop
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
1 được truyền từ Board component xuống.
Bây giờ chúng ta sẽ sử dụng cơ chế truyền prop một lần nữa. Ta sẽ sửa đổi Board component để Square có thể hiển thị giá trị hiện tại của nó [
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
02,
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
10 hoặc
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
11]. Ta đã có mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
12 trong hàm constructor của Board component, bây giờ ta sẽ sửa đổi hàm
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
0 một chút để nó có thể đọc được mảng squares:
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
4
Xem code chi tiết tại thời điểm này
Mỗi Square component bây giờ đã nhận prop
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
1 với giá trị là
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
02,
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
10 hoặc
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
11 [ô trống].
Tiếp theo, ta cần thay đổi một chút để biết cần làm gì ra khi Square được bấm. Hiện tại, Board component đã biết ô nào đã được bấm. Giờ ta sẽ cần cập nhật lại state của Board component khi Square component được bấm. Như đã nói thì state là riêng tư đối với từng component mà nó được nắm giữ, vì vậy chúng ta sẽ không thể cập nhật trực tiếp state của Board component từ Square component.
Thay vào đó, ta sẽ truyền một hàm từ Board xuống Square và từ Square component ta sẽ gọi hàm đó khi nó được bấm. Lúc này phương thức
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
0 sẽ được thay đổi như sau:
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
5
Chú ýChúng ta viết lại phần tử được trả ra trên nhiều dòng để dễ đọc hơn, và thêm một ngoặc tròn sau return để tránh bị lỗi tự thêm dấu chấm phẩy sau
19 của JavaScript.
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
Bây giờ, ta sẽ truyền hai props từ Board xuống Square:
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
1 và
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
8. Prop
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
8 là một hàm mà Square component có thể gọi khi nó được bấm. Chúng ta sẽ thay đổi code của Square component như sau:
- Thay thế
class Square extends React.Component { render[] { return [
]; } }console.log['click']}> {this.props.value}
9 bằng
class Square extends React.Component { render[] { return [
]; } }console.log['click']}> {this.props.value}
8 trong phương thức
8 của Square componentclass Board extends React.Component { renderSquare[i] {
}return ; }
- Thay thế
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
26 bằng
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
27 trong phương thức
8 của Square componentclass Board extends React.Component { renderSquare[i] {
}return ; }
- Xóa hàm khởi tạo
5 trong Square component vì Square component bây giờ không còn nắm giữ state nào nữaclass Square extends React.Component { render[] { return [
]; } }console.log['click']}> {this.props.value}
Sau những thay đổi trên, Square component sẽ như sau:
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
6
Khi một Square được bấm, hàm
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
8 cung cấp bởi Board sẽ được gọi. Dưới đây là tổng kết lại những gì ta đã đạt được đến thời điểm này:
- Prop
class Square extends React.Component { render[] {
} }return [ {this.props.value} ];
8 trong DOM có sẵn
32 component cho phép React lắng nghe sự kiện nhấp chuột.cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
- Khi button được bấm, React sẽ gọi hàm
class Square extends React.Component { render[] {
} }return [ {this.props.value} ];
8 được định nghĩa trong phương thức
8 của Square component.class Board extends React.Component { renderSquare[i] {
}return ; }
- Hàm xử lý sự kiện này sẽ gọi hàm
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
27. Prop
8 của Square component được chỉ định bởi Board component.class Square extends React.Component { render[] {
} }return [ {this.props.value} ];
- Khi Board componet truyền prop
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
37 xuống Square component, Square sẽ gọi hàm
38 khi nó được bấm.cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
- Chúng ta chưa hề định nghĩa phương thức
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
39 vì vậy code của chúng ta sẽ không chạy được ở thời điểm hiện tại. Khi bấm vào một Square, bạn sẽ thấy lỗi được trả ra trên màn hình dạng như “this.handleClick is not a function”.
Chú ý
Thuộc tính [props]
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
8 của thẻ DOM
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
9 có một ý nghĩa đặc biệt đối với React vì nó là component có sẵn. Đối với những component tự định nghĩa như Square component thì việc đặt tên prop này là tùy ý. Chúng ta có thể đặt tên tùy ý cho prop
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
8 của Square component hay hàm
cd my-app
cd src
If you're using a Mac or Linux:
rm -f
Or, if you're on Windows:
del
Then, switch back to the project folder
cd ..
43 của Board component mà code vẫn chạy với cùng một kết quả. Trong React, ta thường hay sử dụng
cd my-app
cd src
If you're using a Mac or Linux:
rm -f
Or, if you're on Windows:
del
Then, switch back to the project folder
cd ..
44 cho những prop thể hiện cho sự kiện và
cd my-app
cd src
If you're using a Mac or Linux:
rm -f
Or, if you're on Windows:
del
Then, switch back to the project folder
cd ..
45 cho những phương thức xử lý sự kiện.Khi thử bấm vào một Square component, chúng ta sẽ nhận được lỗi vì hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
43 chưa được định nghĩa. Bây giờ, ta sẽ thêm hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
43 vào Board component như sau:
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
7
Xem code chi tiết tại bước
Sau những thay đổi trên, chúng ta đã có thể bấm vào các ô Square component để điền giá trị ‘X’ vào nó. Và state bây giờ được nắm giữ bởi Board component thay vì trong từng Square component. Khi state của Board component thay đổi, các Square component sẽ được render lại một cách tự động. Giữ state của tất cả các ô vuông trong Board component sẽ giúp ta có thể xác định ai sẽ là người thắng cuộc ở bước tiếp theo.
Khi các Square component không còn nắm giữ state nữa, Square component nhận vào giá trị từ Board component và thông báo cho Board component khi nó được bấm. Trong thuật ngữ của React, các Square component là những controlled component. Board component có toàn quyền kiểm soát chúng.
Chú ý rằng trong hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
43, chúng ta sử dụng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
49 để tạo ra một bản sao của mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
12 và sửa đổi trên bản sao đó chứ không thay đổi trực tiếp mảng squares. Chúng tôi sẽ giải thích kỹ hơn vì sao cần tạo ra một bản sao của
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
12 trong phần tiếp theo.
Tại sao tính bất biến là quan trọng
Trong phần code ví dụ trước, ta đã sử dụng hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
49 để tạo ra bản sao của mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
12 thay vì sửa trực tiếp nó. Giờ ta sẽ thảo luận về tính bất biến và vì sao tính bất biến lại quan trọng.
Thông thường, ta có 2 hướng tiếp cận đối với việc thay đổi dữ liệu. Hướng tiếp cận đầu tiên đó là thay đổi [mutate] trực tiếp giá trị của dữ liệu. Hướng tiếp cận thứ hai đó là thay dữ liệu hiện có bằng một bản sao của nó và sửa đổi trên bản sao đó.
Sửa đổi dữ liệu trực tiếp
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
8
Sửa đổi dữ liệu gián tiếp
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
9
Kết quả cuối cùng mà ta thu được là như nhau, tuy nhiên với việc thay đổi dữ liệu gián tiếp, ta sẽ thu được một vài lợi ích như sau.
Đơn giản hóa các tính năng phức tạp
Tính bất biến giúp các tính năng phức tập dễ để triển khai hơn. Ở cuối bài hướng dẫn này, chúng ta sẽ triển khai tính năng “quay về bước trước đó [time travel]” cho phép nhìn lại toàn bộ những bước đi trong khi chơi tic-tac-toe và “nhảy về” một bước bất kỳ trước đó. Tính năng này không chỉ có trong trò chơi này mà undo/redo còn là yêu cầu cơ bản và xuất hiện ở rất nhiều ứng dụng. Việc trách sửa đổi trực tiếp dữ liệu cho phép chúng ta lưu lại được giá trị trước đó của nó và có thể tái sử dụng chúng khi cần thiết.
Phát hiện thay đổi
Việc phát hiện thay đổi trên mutable object là rất khó vì nó đã được sửa đổi trực tiếp. Việc phát hiện này đòi hỏi so sánh mutable object với bản copy của nó trước khi được sửa đổi trong khi ta không hề lưu trữ nó.
Việc phát hiện thay đổi trên immutable object là dễ dàng hơn khá nhiều. Nếu tham chiếu của immutable object hoàn toàn khác với chính nó trước đó thì nó này đã được thay đổi.
Xác định khi nào component render lại trong React
Lợi ích chính của tính bất biến là giúp chúng ta xây dựng các pure component trong React. Những dữ liệu bất biến có thể dễ dàng xác định khi nào nó thay đổi, từ đó giúp ta xác định được khi nào một component cần phải render lại.
Bạn có thể đọc thêm về
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
54 và làm sao để xây dựng các pure component qua bài viết
Function Components
Bây giờ ta sẽ sửa đổi Square component để nó trở thành một functional component.
Trong React, function components là một cách đơn giản để viết các component chỉ chứa phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 và không chứa bất kỳ state nào. Thay vì phải định nghĩa một lớp kế thừa
class Board extends React.Component {
renderSquare[i] {
return ; }
}
6, chúng ta có thể viết một hàm nhận vào các prop để hiển thị ra giao diện. Các Functional component thường sẽ ít tẻ nhạt hơn class component, và khá nhiều component có thể viết dưới dạng này.
Chuyển class Square thành dạng function:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
0
Ta đổi
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
57 thành
class Board extends React.Component {
renderSquare[i] {
return ; }
}
7 trong toàn bộ component.
Xem code chi tiết tại bước này
Chú ýKhi chuyển Square component thành dạng function component, chúng ta đã đổi
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
59 thành dạng gọn hơn
60 [bỏ ngoặc đơn ở cả hơn vế].
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
Lượt chơi
Bây giờ, Ta đang có một thiếu xót quan trọng: Các ký tự “O” chưa thể được đánh dấu trên bàn cờ.
Chúng ta sẽ đặt mặc định bước đi đầu tiên sẽ thuộc về “X”. Và để làm điều đó, ta có thể sửa lại giá trị khởi tạo của state trong Board constructor như sau:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
1
Sau mỗi bước đi,
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
61 [một giá trị đúng sai [boolean]] sẽ được đảo ngược để xác định xem người chơi nào sẽ đi tiếp và state của trò chơi sẽ thay đổi và được lưu lại. Ta sẽ cập nhật lại hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
43 để đảo ngược giá trị của
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
61:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
2
Với thay đổi này, “X” và “0” đã có thể có lượt đi của mình.
Tiếp theo, hãy cùng thay đổi dòng “trạng thái” trong phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Board component để hiển thị lượt chơi tiếp theo thuộc về ai:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
3
Sau những thay đổi trên, bạn sẽ có một Board component như sau:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
4
Xem code chi tiết tại bước này
Xác định người thắng cuộc
Hiện tại, ta đã hiển thị được lượt chơi tiếp theo thuộc về ai, ta cũng cần phải hiện ai là người thắng cuộc và trò chơi kết thúc. Bạn hãy sao chép đoạn code dưới đây và dán vào cuối file:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
5
Hàm trên sẽ nhận vào một mảng ứng với 9 ô vuông để sẽ kiểm tra xem ai là người thắng cuộc. Hàm này sẽ trả ra
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
02,
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
10 hoặc
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
11 nếu chưa ai thắng.
Chúng ta sẽ gọi hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
68 trong phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Board để kiểm tra xem có người chơi thắng cuộc hay chưa. Nếu một người chơi chiến thắng, ta sẽ hiển thị “Winner: X” hoặc “Winner O”. Ta sẽ thay thế dòng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
70 được định nghĩa trong phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Board component bằng đoạn code dưới đây:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
6
Bây giờ ta sẽ thay đổi phương thức
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
43 để bỏ qua việc xử lý click khi có một người chiến thắng hoặc ô đó đã có giá trị được điền:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
7
Xem code chi tiết tại bước này
Xin chúc mừng! Bạn đã xây dựng thành công trò chơi tic-tac-toe. Và bạn cũng vừa học được lý thuyết cơ bản của React. Vì thế bạn đã trở thành một người thắng cuộc thực sự ở đây rồi đó.
Thêm lịch sử bước đi
Như một bài tập cuối, hãy làm trò chơi có thể “quay lại các bước trước đó” [“go back in time”].
Lưu lại lịch sử các bước đi
Nếu chúng ta thay đổi trực tiếp mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
12, việc quay về bước trước sẽ là rất khó.
Tuy nhiên, chúng ta đã sử dụng hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
74 để tạo một mảng mới được sao chép từ mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
12 sau mỗi bước đi và . Việc này sẽ cho phép ta lưu lại lịch sử các thay đổi của mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
12, từ đó quay lại một trong các bước đi đã xảy ra.
Ta sẽ lưu lại lịch sử thay đổi của mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
12 bằng mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78. Mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78 sẽ chứa toàn bộ các trạng thái của bàn cờ, từ bước đi đầu tiên cho đến bước cuối cùng, và nó sẽ có dạng:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
8
Bây giờ, ta cần xác định xem component nào sẽ thích hợp để lưu state
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78
Đưa state lên component cha một lần nữa
Chúng ta sẽ muốn component ở mức độ cao nhất — Game component, sẽ hiển thị lịch sử các bước đi. Nó sẽ cần đọc
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78 để làm điều đó, vì vậy ta sẽ đặt state
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78 ở Game component.
Đặt state
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78 ở Game component sẽ cho phép chúng ta xóa state
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
12 từ component con của nó là Board component. Như chúng ta đã thực hiện ở phần — từ Square component lên Board component, giờ ta sẽ đưa state từ Board lên Game component. Điều này sẽ khiến cho Game component có toàn quyền xử lý dữ liệu của Board component và giúp nó có thể báo cho Board component biết khi nào cần render bước đi trước đó từ mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78
Đầu tiên, ta sẽ khởi tạo state ban đầu của Game component trong phương thức constructor của nó:`
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
9
Tiếp theo, chúng ta sẽ có Board component nhận vào các prop
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
12 và
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
8 từ Game component. Do chúng ta có một hàm xử lý sự kiện click duy nhất ở Board cho rất nhiều Squares nên ta sẽ cần truyền vị trí của mỗi Square trong hàm
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
8 để chỉ định ô nào được bấm. Dưới đây là các bước cần thiết để sửa đổi Board component:
- Xoá hàm
5 trong Board component.class Square extends React.Component { render[] { return [
]; } }console.log['click']}> {this.props.value}
- Thay thế
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
90 thành
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
91 trong phương thức
0 của Board component.class Square extends React.Component { render[] {
} }return [ {this.props.value} ];
- Thay thế
cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
38 bằng
94 trong phương thức render của Board component.cd my-app cd src
If you're using a Mac or Linux:
rm -fOr, if you're on Windows:
delThen, switch back to the project folder
cd ..
Giờ đây Board component sẽ như sau:
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
0
Bây giờ ta sẽ cập nhật lại hàm
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Game component để sử dụng lịch sử gần nhất để xác định và hiển thị trạng thái hiện tại của trò chơi:
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
1
Do Game component hiện tại đang hiển thị trạng thái của trò chơi, vì thế ta có thể xóa đoạn code tương tự trong phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Board component. Sau khi tái cấu trúc, phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Board component sẽ như sau:
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
2
Cuối cùng, chúng ta cần chuyển hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
43 từ Board component lên Game component. Chúng ta cũng cần sửa lại hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
43 vì cấu trúc state của Game component khác với của Board component trước đó. Trong phương thức
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
43, ta sẽ nối thêm vào
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78 một lịch sử mới.
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
3
Chú ýKhông giống như phương thức
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css';
02 [bạn sẽ thấy nó khá tương đồng], phương thức
03 không thêm trực tiếp vào mảng dữ liệu gốc, vì vậy chúng ta sẽ dùng nó.
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css';
Tại bước này, Board component chỉ cần hai phương thức
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
0 và
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8. State của trò chơi và phương thức
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
43 sẽ nằm ở Game component.
Xem code chi tiết tại bước này
Hiển thị lịch sử các bước đi
Giờ ta đã có lịch sử các bước đi của trò chơi tic-tac-toe, tiếp theo ta sẽ cần hiển thị nó.
Như đã nói ở trên thì các React element là các đối tượng của các lớp trong JavaScript [first-class JavaScript object]; và chúng ta cần truyền nó qua lại trong ứng dụng của chúng ta. Để render nhiều item trong React, ta có thể sử dụng một mảng các React element.
Trong JavaScript,
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
07 là cách rất phổ biến để biến đổi dữ liệu thành một mảng dữ liệu khác, ví dụ:
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
4
Sử dụng phương thức
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
08, ta có thể biến lịch sử các bước đi thành các React element biểu diễn bằng các nút bấm [button] trên màn hình, khi bấm vào những button này sẽ “nhảy” về một bước trước đó.
Hãy
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
08 mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78 trong phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Game component:
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
5
Xem code chi tiết tại bước này
Với mỗi lịch sử bước đi trong game tic-tac-toe, ta tạo ra một danh sách các
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
12 chứa một
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
9. Button sẽ có một hàm xử lý
class Square extends React.Component {
render[] {
return [
{this.props.value}
];
}
}
8, hàm này sẽ gọi phương thức
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
15. Hiện tại ta chưa có hàm
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
16. Đến bước này, bạn sẽ thấy một danh sách các bước đi được hiển thị trên màn hình và cảnh báo trong console của công cụ cho nhà phát triển với thông điệp như sau:
Khi chúng ta loop qua mảng
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78,
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
18 biến này ánh xạ tới value của element
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78. Và
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
20 ánh xạ tới
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78 element index. Chúng ta chỉ quan tâm tới
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
20 ở đây, vì thế
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
18 sẽ không được gán.
Warning: Each child in an array or iterator should have a unique “key” prop. Check the render method of “Game”.
Cùng thảo luận xem dòng cảnh báo trên có nghĩa là gì nhé.
Chọn một khóa [key]
Khi render một danh sách, React lưu lại một vài thông tin về mỗi phần tử được render. Khi cập nhật lại danh sách, React cần xác định xem thay đổi là gì, nó có thể là thêm, xóa, sắp xếp lại hoặc sửa lại danh sách các phần tử.
Ví dụ thay đổi từ
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
6
thành
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
7
Ngoài cập nhật về số lượng task, một người đọc những thay đổi trên sẽ có thể phát biểu rằng thứ tự của Alexa và Ben được đổi chỗ cho nhau và Claudia được thêm vào giữa Alexa và Ben. Tuy nhiên, React là một chương trình máy tính và nó sẽ không thể hiểu được ý của chúng ta. Vì React không thể biết ý định của chúng là gì, nên ta cần phải chỉ định một khóa [key] cho từng phần tử để phân biệt nó với các phần tử còn lại. Một lựa chọn về khóa cho ví dụ trên có thể là string
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
24,
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
25,
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
26. Nếu ta hiển thị dữ liệu từ cơ sở dữ liệu thì ID của Alexa, Ben và Claudia có thể được sử dụng để làm khóa
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
8
Khi một danh sách được render lại, React sẽ lấy mỗi khóa của phần tử , tìm kiếm trong danh sách các phần từ trước đó. Nếu danh sách hiện tại có chứa một khóa không thuộc danh sách trước đó, React sẽ tạo thêm một component. Nếu danh sách hiện tại không chứa khóa tồn tại trong danh sách trước đó, React sẽ xóa component đó. Nếu khóa tồn tại trong cả hai danh sách, React sẽ cập nhật lại component đó. Các Khóa giúp React định danh được mỗi component và từ đó duy trì được state và props giữa các lần render. Nếu khóa của component thay đổi, component sẽ được xóa đi và tạo lại với giá trị state mới.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
27 [khóa] là từ thuộc tính [prop] đặc biệt và reserved trong React [cùng với
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
28, một tính năng nâng cao]. Khi một phần tử được tạo ra, React sẽ tách prop
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
27 từ
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
30. React tự động sử dụng
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
27 để quyết định xem component nào sẽ được cập nhật. Một component không thể điều tra [inquire] và
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
27 của nó.
Chúng tôi đặc biệt khuyến khích bạn thêm props key khi xây dựng các danh sách động. Nếu bạn không có khóa phù hợp, hãy cân nhắc việc tái cấu trúc dữ liệu của bạn.
Nếu không có khóa nào được chỉ định, React sẽ hiện một cảnh báo và sử dụng chỉ số của mảng để làm khóa. Sử dụng chỉ số của mảng để làm khóa sẽ có thể xảy ra một lỗi khi sắp sếp lại thứ tự hoặc thêm/xóa phần tử. Chỉ định
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
33 có thể giúp không hiện cảnh báo nhưng vẫn sẽ xảy ra lỗi tương tự và nó không được khuyến khích sử dụng trong hầu hết các trường hợp.
Các khóa không cần phải là duy nhất trong toàn ứng dụng; nó chỉ cần là duy nhất giữa các component cùng cấp.
Triển khai tính năng nhảy lùi bước đi
Trong lịch sử các bước đi của trò chơi tic-tac-toe, mỗi bước đi có một ID gắn liền với nó: Số thứ tự của bước đi. Các bước đi không bao giờ được sắp xếp lại, xóa hay thêm vào giữa, vì vậy ta có thể sử dụng chỉ số mảng để làm khóa trong trường hợp này.
Trong phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Game component, ta có thể thêm khóa
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
35 và cảnh báo của react về khóa sẽ biến mất:
class ShoppingList extends React.Component {
render[] {
return [
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
];
}
}
// Example usage:
9
Xem code chi tiết tại bước này
Bấm vào bất kỳ một button nào trên danh sách các bước đi sẽ xảy ra lỗi bởi vì hàm
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
36 chưa được định nghĩa. Trước khi viết hàm
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
37, ta sẽ thêm
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
38 vào state của Game component để biết bước đi nào đang được hiển thị.
Đầu tiên, thêm
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
39 vào state ban đầu trong hàm
class Square extends React.Component {
render[] {
return [
console.log['click']}> {this.props.value}
];
}
}
5 của Game component:
return React.createElement['div', {className: 'shopping-list'},
React.createElement['h1', /* ... h1 children ... */],
React.createElement['ul', /* ... ul children ... */]
];
0
Tiếp theo, chúng ta sẽ định nghĩa hàm
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
37 trong Game component để cập nhật
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
38. Chúng ta cũng cần đặt
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
43 là true nếu state tiếp theo của
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
38 là chẵn:
return React.createElement['div', {className: 'shopping-list'},
React.createElement['h1', /* ... h1 children ... */],
React.createElement['ul', /* ... ul children ... */]
];
1
Notice in
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
37 method, we haven’t updated
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
78 property of the state. That is because state updates are merged or in more simple words React will update only the properties mentioned in
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
04 method leaving the remaining state as is. For more info .
Bây giờ ta sẽ cần thay đổi hàm
cd my-app
cd src
# If you're using a Mac or Linux:
rm -f *
# Or, if you're on Windows:
del *
# Then, switch back to the project folder
cd ..
43 của Game component một chút.
State
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
38 mà chúng ta vừa thêm phản ánh bước đi hiện tại của người chơi. Sau khi một bước đi diễn ra, ta cần cập nhật lại
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
38 như sau
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
51. Việc này đảm bảo chúng ta sẽ không bị hiển thị cùng một bước đi sau khi một bước đi mới được tạo ra.
Ta cũng sẽ thay thế
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
52 bằng
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
53. Việc này đảm bảo nếu ta “quay về một bước đi trước đó” và sau khi ta đi một bước mới từ điểm đó, mảng history “tương lai” sẽ không bị sai.
return React.createElement['div', {className: 'shopping-list'},
React.createElement['h1', /* ... h1 children ... */],
React.createElement['ul', /* ... ul children ... */]
];
2
Cuối cùng, ta sẽ sửa lại phương thức
class Board extends React.Component {
renderSquare[i] {
return ; }
}
8 của Game component, thay vì luôn render bước đi cuối cùng thì giờ sẽ render bước đi được chọn từ
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
38:
return React.createElement['div', {className: 'shopping-list'},
React.createElement['h1', /* ... h1 children ... */],
React.createElement['ul', /* ... ul children ... */]
];
3
Nếu bạn bấm vào bất kỳ bước đi nào trong lịch sử các bước đi, bàn giờ sẽ ngay lập tức cập nhật và hiển thị đúng như trước khi bước đi đó diễn ra.
Xem code chi tiết tại bước này
Tổng kết
Chúc mừng! Bạn vừa tạo thành công trò chơi tic-tac-toe với các tính năng:
- Chơi game tic-tac-toe,
- Tìm ra người chơi thắng cuộc,
- Lưu trữ lịch sử các bước đi,
- Cho phép người chơi xem lại các bước đi.
Làm tốt lắm! Chúng tôi hy vọng bây giờ bạn cảm thấy bạn đã có một hiểu biết đáng kể về cách thức hoạt động của React.
Xem lại phiên bản cuối cùng tại đây: Final Result.
Nếu có thời gian hoặc muốn thực hành nhiều hơn nữa, dưới đây là một số ý tưởng [độ khó tăng dần] để bạn cải thiện trò chơi tic-tac-toe:
- Hiển thị vị trí của mỗi bước đi dưới dạng [cột, dòng] trong lịch sử các bước đi.
- In đậm bước hiện tại trong danh sách các bước đi.
- Viết lại Board sử dụng hai vòng lặp để tạo ra Square thay vì hardcode như hiện nay.
- Thêm toogle button cho phép sắp sếp các bước đi theo thứ tự tăng hoặc giảm.
- Khi một người chơi thắng cuộc, highlight ba ô vuông dẫn đến chiến thắng.
- Khi không ai thắng cuộc, hiển thị thông báo kết quả hòa.
Xuyên suốt bài hướng dẫn này, chúng tôi đã đề cập các khái niệm trong React bao gồm elements, components, props và state. Để tìm hiểu chi tiết hơn về từng khái niệm, bạn có thể xem phần còn lại của tài liệu. Để tìm hiểu sâu hơn về định nghĩa components, hãy ghé thăm
class Board extends React.Component {
renderSquare[i] {
return ; }
}
6 API reference.