Tạo phần tử React với JSX, vòng lặp và điều kiện
- 22-09-2022
- Toanngo92
- 0 Comments
Trong bài viêt này này, chúng ta sẽ tìm hiểu cách mô tả các phần tử bằng JSX. JSX là một cú pháp trừu tượng giống HTML trong mã JavaScript của mình và cho phép bạn xây dựng các thành phần React trông giống như HTML tiêu chuẩn. JSX là ngôn ngữ tạo template của các phần tử React và do đó là nền tảng cho bất kỳ các markup(thẻ) nào mà React sẽ hiển thị vào ứng dụng của bạn.
Vì JSX cho phép bạn cũng viết JavaScript trong markup của mình, bạn sẽ có thể tận dụng các hàm và phương thức JavaScript, bao gồm ánh xạ mảng (hàm map) và logic điều kiện hiển thị markup.
Ở bài này, mục tiêu sẽ là nắm bắt các sự kiện nhấp chuột (on click) vào các nút trong phần đánh dấu và nắm bắt các trường hợp khi cú pháp không khớp chính xác với HTML chuẩn, chẳng hạn như với các lớp CSS. Ở cuối hướng dẫn này, bạn sẽ có một ứng dụng đang hoạt động sử dụng nhiều tính năng JSX để hiển thị danh sách các phần tử có trình nghe nhấp chuột được tích hợp sẵn. Đây là một mẫu phổ biến trong các ứng dụng React mà bạn sẽ sử dụng thường xuyên trong quá trình học framework. Bạn cũng sẽ có thể kết hợp các phần tử HTML chuẩn cùng với JavaScript để xem cách React cung cấp cho bạn khả năng tạo các đoạn mã nhỏ, có thể sử dụng lại.
Khởi tạo một React element
Hãy bắt đầu với cấu trúc dự án mới, mở file index.js để xem qua mã nguồn:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// 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();
Tạm hiểu, root là một đối tượng ReactDom (có thể test bằng cách console.log biến root ).
Biến root gọi phương thức (hàm) render và hàm này nhận vào tham số là một đoạn mã JSX, chúng ta thấy bên trong cấấu trúc có chứa markup <App/>. Markup này thể hiện đối tượng App bên trong file App.js được import từ những dòng đầu tiên của mã nguồn.
Tiếp tục mở file file App.js
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
Thử thay toàn bộ cấu trúc JSX bên trong tham số return, như sau và load lại ứng dụng:
import logo from './logo.svg';
import './App.css';
function App() {
return null;
}
export default App;
Chúng ta lưu và test ứng dụng trên trình duyệt, trình duyệt sẽ trả ra trang trắng và môi trường dòng lệnh trả ra 1 cảnh báo:
WARNING in [eslint]
src\App.js
Line 1:8: 'logo' is defined but never used no-unused-vars
// ý nghĩa cảnh báo này là chúng ta import logo.svg vào dự án nhưng không sử dụng
Tiếp theo, chúng ta thử với một cấu trúc JSX đơn giản nhất:
import './App.css';
function App() {
return (<h1>Hello, world!</h1>);
}
export default App;
Chúng ta thử test bằng cách bổ sung một thẻ bên trong cấu trúc JSX giống HTML thuần và lưu lại thử như sau:
import './App.css';
function App() {
return (
<h1>Hello, world!</h1>
<p>Xin chao</p>
)
}
export default App;
Terminal sẽ trả ra thông báo lỗi:
ERROR in [eslint]
src\App.js
Line 15:34: Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>? (15:34)
Giải thích:
Khi bạn trả về JSX từ một hàm hoặc câu lệnh, bạn phải trả về một phần tử duy nhất. Phần tử đó có thể có các phần tử con lồng nhau, nhưng phải có một phần tử cấp cao nhất. Trong trường hợp này, bạn đang trả về hai phần tử cùng cấp và là cấp cao nhất.
Cách khắc phục là một thay đổi mã nguồn. Bao quanh mã bằng một thẻ trống. Thẻ trống là một phần tử HTML không có bất kỳ từ nào. Mã nguồn sẽ hết lỗi nếu cấu trúc thế này:
import './App.css';
function App() {
return (
<>
<h1>Hello, world!</h1>
<p>Xin chao</p>
</>
)
}
export default App;
Mục lục
Định kiểu cho React Element
Tham khảo docs: Styling and CSS – React (reactjs.org)
Với mã nguồn mẫu phía trên, chúng ta quan tâm tới cú pháp dòng đầu tiên
import './App.css';
Giải thích: chúng ta có thể viết CSS để định kiểu cho file App.js thông qua file App.css cùng cấp thư mục.
Chúng ta mở file App.css, xóa toàn bộ code cũ và cập nhật vài thuộc tính định kiểu cho thẻ h1 và thẻ p để kiểm thử:
h1{
font-size: 3rem;
}
p{
color: red;
}
Output:
Tuy nhiên, nếu chúng ta muốn set màu nền markup ôm cả thẻ <h1> và <p>, chúng ta không thể sử dụng cấu trúc thẻ rỗng mà buộc phải đặt tên cho thẻ và thêm thuộc tính cho nó. ví dụ:
import './App.css';
function App() {
return (
<div>
<h1>Hello, world!</h1>
<p>Xin chao</p>
</div>
)
}
export default App;
Tiếp theo, chúng ta cần thêm tên class để tiến hành định kiểu cho thẻ div. Các thuộc tính (Attribute) trong JSX có một chút khác biệt với HTML. Nếu bạn muốn thêm một class vào một phần tử HTML cấu trúc thẻ sẽ như sau (mình sẽ bắt trước bootstrap, thêm một class container vào đây):
<div class="container">
Nhưng vì JSX là JavaScript nên nó có một số hạn chế. Một trong những hạn chế là JavaScript có các từ khóa dành riêng. Điều đó có nghĩa là bạn không thể sử dụng một số từ nhất định trong bất kỳ mã JavaScript nào. Ví dụ: bạn không thể tạo một biến có tên null vì từ khóa đó là từ khóa nóng và đã được đặt tên từ trước.
Một trong những từ dành riêng là class. Các thuộc tính React xoay quanh một số từ dành riêng này bằng cách thay đổi nó một chút. Thay vì thêm thuộc tính class như HTML thuần, bạn sẽ thêm tên thuộc tính className. Theo quy tắc, nếu một thuộc tính không hoạt động như mong đợi, hãy kiểm tra hoa thường, (hay gặp lỗi camel case). Một thuộc tính hơi khác là thuộc tính for mà bạn muốn sử dụng cho thẻ <label>. Có một vài trường hợp khác, tuy nhiên, danh sách này khá ngắn.
Lưu ý: Trong React, các thuộc tính thường được gọi là props. Props là các phần dữ liệu mà bạn có thể truyền cho các thành phần tùy chỉnh khác. Chúng trông giống như các thuộc tính ngoại trừ việc chúng không khớp với bất kỳ thông số kỹ thuật HTML nào. Trong hướng dẫn này, chúng ta sẽ gọi chúng là các thuộc tính (attribute) vì chúng chủ yếu được sử dụng giống như các thuộc tính HTML chuẩn. Điều này sẽ phân biệt chúng với các props không hoạt động như các thuộc tính HTML, sẽ được đề cập ở các bài hướng dẫn tiếp theo.
Giờ chúng ta bắt đầu đưa thuộc tính className và thẻ div và định kiểu:
Mã nguồn App.js
import './App.css';
function App() {
return (
<div className="container">
<h1>Hello, world!</h1>
<p>Xin chao</p>
</div>
)
}
export default App;
Mã nguồn App.css
h1{
font-size: 3rem;
margin: 0px;
}
p{
color: red;
}
.container{
background: blueviolet;
}
Output:
Thuộc tính className là duy nhất trong React. Bạn có thể thêm hầu hết các thuộc tính HTML vào JSX mà không có bất kỳ thay đổi nào. Ví dụ: thêm id vào một thẻ bên trong cấu trúc của bạn. Nó sẽ giống như HTML tiêu chuẩn:
import './App.css';
function App() {
return (
<div className="container">
<h1>Hello, world!</h1>
<p id="helloparagraph">Xin chao</p>
</div>
)
}
export default App;
Output:
Chúng ta hình dung, cú pháp JSX trông giống như đánh dấu tiêu chuẩn, nhưng ưu điểm của JSX là mặc dù nó trông giống HTML, nhưng nó có sức mạnh của JavaScript. Điều đó có nghĩa là bạn có thể gán các biến và tham chiếu chúng trong các thuộc tính của mình, điều này HTML thuần không làm đưojc. Để tham chiếu một thuộc tính, hãy bọc nó bằng dấu ngoặc nhọn – {} – thay vì dấu nháy kép hoặc nháy đơn.
Ví dụ:
import './App.css';
function App() {
const _testid = 'helloparagraph';
return (
<div className="container">
<h1>Hello, world!</h1>
<p id={_testid}>Xin chao</p>
</div>
)
}
export default App;
Output:
Như chúng ta thấy, output trả ra giá trị của thuộc tính id chính bằng giá trị của biến _testid vừa khai báo, điều này HTML không thể làm được mà phải thông qua các cú pháp đối tượng dom trong javascript thuần. Đây chính là điểm mạnh của reactJS giúp giảm tối đa thời gian phát triển ứng dụng.
Ngoài ra, chúng ta có có thể định kiểu trong ReactJS thông qua các biến và thuộc tính style. Ví dụ:
import './App.css';
function App() {
const _testid = 'helloparagraph';
const style_heading = {
'font-size': '40px',
'color' : '#fff',
'text-align' : 'center'
}
return (
<div className="container">
<h1 style={style_heading}>Hello, world!</h1>
<p id={_testid}>Xin chao</p>
</div>
)
}
export default App;
Tuy nhiên, không nên sử dụng trong thực tế, mặc dù mình hay dùng trong các ví dụ, vì compiler của reactJS sẽ warning lỗi. Và tất nhiên, bạn hoàn toàn có thể xây dựng một cấu trúc HTML phức tạp bằng JSX, nhưng hãy ghi nhớ markup cấp cao nhất luôn luôn chỉ có một thẻ
Thêm sự kiện vào React Element và xử lý sự kiện
Bây giờ bạn đã có một trang cơ bản với thông tin, đã đến lúc thêm một vài sự kiện vào đó. Có nhiều trình xử lý sự kiện mà bạn có thể thêm vào các phần tử HTML. React cung cấp cho bạn quyền truy cập vào tất cả những thứ này. Vì mã JavaScript của bạn được kết hợp với markup của bạn, bạn có thể dễ dàng thêm các sự kiện trong khi vẫn giữ cho mã của mình được tổ chức tốt.
Thông thuờng, trong HTML và Javascript thuần, chúng ta có thể thêm thêm trình xử lý sự kiện onclick như một thuộc tính trong thẻ. Điều này cho phép bạn thêm một số mã JavaScript trực tiếp vào phần tử của mình thay vì đính kèm trình xử lý sự kiện của javascrip thuần(event listener). Còn với ReactJS, chúng ta có thuộc tính onClick. Ví dụ một cấu trúc HTML sau:
import './App.css';
function App() {
const style_heading = {
'font-size': '40px',
'color': '#fff',
'text-align': 'center'
}
const style_btn_box = {
'display': 'flex',
'justify-content': 'center'
}
const showAlertAboutMe = (event) => {
alert("Alert 2")
}
return (
<div className="container">
<h1 style={style_heading}>Hello, world!</h1>
<div style={style_btn_box}>
<button data-btn="Hello I'm here" onClick={(event) => alert(event.target.getAttribute('data-btn'))}>Show alert 1</button>
<button data-btn="Click to button 2" onClick={(event) => alert(event.target.getAttribute('data-btn'))}>Show alert 2</button>
<button onClick={(event) => showAlertAboutMe(event)}>About me</button>
</div>
</div>
)
}
export default App;
Với đoạn code mẫu trên, giao diện của mình sẽ có 3 nút nằm cạnh nhau bên trong một khối, khi bấm vào từng nút sẽ mở hộp thoại với các thông tin khác nhau, với nút about me, khi sự kiện xảy ra gọi một hàm tự định nghĩa. Đây là ví dụ cơ bản nhất về bắt sự kiện trong ReactJS.
Ánh xạ qua dữ liệu để tạo phần tử React
Ở phần này, chúng ta sẽ tìm hiểu cách kết hợp các markup với JavaScript để tạo markup động giúp giảm mã và cải thiện khả năng đọc. Bạn sẽ cấu trúc lại mã của mình thành một mảng mà bạn sẽ sử dụng vòng lặp để tạo các phần tử HTML.
JSX không giới hạn bạn trong một cú pháp giống HTML. Nó cũng cung cấp cho bạn khả năng sử dụng JavaScript trực tiếp trong markup của bạn.
Tiếp tục mở file App.js và chỉnh sửa mã nguồn thành như sau:
import './App.css';
function App() {
const style_heading = {
'font-size': '40px',
'color': '#fff',
'text-align': 'center'
}
const style_btn_box = {
'display': 'flex',
'justify-content': 'center'
}
const btn_data = [
{
data : "Hello I'm here",
label_text: "Show alert 1"
},
{
data : "Click to button 2",
label_text: "Show alert 2"
},
{
data : "About me",
label_text: "About me"
}
]
const showAlertAboutMe = (event,data) => {
alert(data);
}
return (
<div className="container">
<h1 style={style_heading}>Hello, world!</h1>
<div style={style_btn_box}>
{
btn_data.map((item) => {
return <button data-btn={item.data} onClick={(event) => showAlertAboutMe(event,item.data)}>{item.label_text}</button>
})
}
</div>
</div>
)
}
export default App;
Như chúng ta thấy, reactJS có thể lặp dữ liệu trong mảng và in thẳng ra HTML mà không cần các cú pháp phức tạp như Javascript thuần.
Logic hiển thị phần tử theo điều kiện
Khái niệm này anh em lập trình hay nói chuyện với nhau là if else một dòng, cho phép bạn tạo các thành phần có thể ẩn hoặc hiển thị HTML dựa trên thông tin bổ sung, giúp các thành phần của bạn linh hoạt để xử lý nhiều tình huống.
Đôi khi bạn sẽ cần một thành phần để hiển thị thông tin trong một số trường hợp chứ không phải các thành phần khác. Ví dụ: bạn có thể chỉ muốn hiển thị thông báo cảnh báo cho người dùng nếu một số trường hợp nhất định là đúng hoặc bạn có thể muốn hiển thị một số thông tin tài khoản cho quản trị viên mà bạn không muốn người dùng bình thường nhìn thấy.
Cú pháp:
{condition && ...}
Ví dụ:
import './App.css';
function App() {
const style_heading = {
'font-size': '40px',
'color': '#fff',
'text-align': 'center'
}
const style_btn_box = {
'display': 'flex',
'justify-content': 'center'
}
const show_btn = false;
const btn_data = [
{
data : "Hello I'm here",
label_text: "Show alert 1"
},
{
data : "Click to button 2",
label_text: "Show alert 2"
},
{
data : "About me",
label_text: "About me"
}
]
const showAlertAboutMe = (event,data) => {
alert(data);
}
return (
<div className="container">
<h1 style={style_heading}>Hello, world!</h1>
<div style={style_btn_box}>
{ show_btn &&
btn_data.map((item) => {
return <button data-btn={item.data} onClick={(event) => showAlertAboutMe(event,item.data)}>{item.label_text}</button>
})
}
</div>
</div>
)
}
export default App;
Với ví dụ phía trên, output sẽ chỉ hiển thị html các nút khi giá trị biến show_btn được gán bằng true.
Bài tập
Bài 1: Tạo ứng dụng React JS với giao diện như hình:
Gợi ý:
khởi tạo 3 component header, footer, main
Cả 3 component nhúng vào một Component App
Bài 2:
Làm ứng dụng todolist như hình: