ReactJS: Cách tạo style cho các component React
Giới thiệu
Trong bài hướng dẫn này, bạn sẽ học ba cách khác nhau để tạo style cho các component của React: Trang kiểu xếp tầng đơn giản (CSS), kiểu nội tuyến với các đối tượng kiểu JavaScript và JSS, một thư viện để tạo CSS bằng JavaScript. Mỗi tùy chọn này đều có ưu điểm và nhược điểm, một số giúp bạn bảo vệ tốt hơn trước các xung đột về style hoặc cho phép bạn ánh xa trực tiếp các prop hoặc dữ liệu động khác. Nhưng tất cả các tùy chọn đều có một điểm chung: Chúng cho phép bạn giữ các style đặc tả component gần với component, giúp các component dễ dàng sử dụng lại trong một dự án hoặc thậm chí trên nhiều dự án không liên quan.
Mỗi tùy chọn này đều dựa trên các thuộc tính CSS. Để sử dụng CSS thuần túy mà không có bất kỳ dữ liệu thời gian chạy nào, bạn có thể import các CSS. Nếu bạn muốn tạo style được tích hợp với component, bạn có thể sử dụng các đối tượng style nội tuyến sử dụng tên thuộc tính CSS làm khóa và kiểu làm giá trị. Cuối cùng, nếu bạn muốn kết hợp, bạn có thể sử dụng thư viện của bên thứ ba, chẳng hạn như JSS để viết CSS của bạn theo cú pháp JavaScript, một khái niệm phần mềm được gọi là CSS-in-JS.
Để minh họa các phương pháp này, bạn sẽ xây dựng một ví dụ component alert
sẽ hiển thị style thành công hoặc style lỗi tùy thuộc vào phần mềm hỗ trợ. Component alert
sẽ lấy bất kỳ số lượng trẻ em nào. Điều này có nghĩa là bạn sẽ cần phải thận trọng với các xung đột về style, vì bạn không có cách nào biết được những style mà các component con sẽ có. Sau khi tạo component alert
, bạn sẽ cấu trúc lại nó bằng cách sử dụng từng tùy chọn tạo style để bạn có thể thấy sự giống và khác nhau giữa các phương pháp.
Bước 1 - Tạo một dự án trống
Trong bước này, bạn sẽ tạo một dự án mới bằng Create React App. Sau đó, bạn sẽ xóa dự án mẫu và các tệp liên quan được cài đặt khi bạn khởi động dự án. Cuối cùng, bạn sẽ tạo một cấu trúc tệp đơn giản để tổ chức các component của mình. Điều này sẽ cung cấp cho bạn một cơ sở vững chắc để xây dựng ứng dụng mẫu của hướng dẫn này để tạo style trong bước tiếp theo.
Để bắt đầu, hãy thực hiện một dự án mới. Bạn mở terminal và chạy tập lệnh sau để cài đặt một dự án mới bằng cách sử dụng create-react-app
:
npx create-react-app styling-tutorial
Sau khi dự án kết thúc, hãy thay đổi vào thư mục:
cd styling-tutorial
Khởi động dự án với lệnh:
npm start
http://localhost:3000/
. Nếu bạn đang chạy điều này từ một máy chủ từ xa, địa chỉ sẽ là .http://your_domain:3000
Trình duyệt của bạn sẽ tải với một ứng dụng React đơn giản được bao gồm như một phần của Create React App:
Bạn sẽ xây dựng một tập hợp các thành phần tùy chỉnh hoàn toàn mới, vì vậy bạn sẽ cần bắt đầu bằng cách xóa một số mã soạn sẵn để bạn có thể có một dự án trống.
Để bắt đầu, hãy mở component src/App.js
. Đây là component gốc được đưa vào trang. Tất cả các component sẽ bắt đầu từ đây. Bạn sửa lại file để trong đó chỉ còn chứa như sau:
import './App.css';
function App() {
return <></>;
}
export default App;
Mở một terminal khác và thực hiện thao tác xóa file logo.svg:
rm src/logo.svg
Tạo thư mục components:
mkdir src/components
Tạo thư mục App:
mkdir src/components/App
Di chuyển các file App.* vào thư mục App:
mv src/App.* src/components/App
Mở file index.js và chỉnh sửa:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App/App';
import * as serviceWorker from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
Lưu file lại và quay lại trang web ta được một trang web trống.
Bây giờ bạn đã hoàn thành dự án Tạo ứng dụng React mẫu, hãy tạo một cấu trúc tệp đơn giản. Điều này sẽ giúp bạn giữ cho các component của bạn cô lập và độc lập.
Tạo một thư mục được gọi components
trong thư mục src
, components sẽ là nơi chứa tất cả các component tùy chỉnh của bạn.
Bước 2 - Tạo style cho một component bằng CSS thuần túy
Trong bước này, bạn sẽ xây dựng một component Alert
mẫu sẽ hiển thị cảnh báo trên trang web. Bạn sẽ tạo style này bằng cách sử dụng CSS thuần túy, bạn sẽ import trực tiếp vào component. Điều này sẽ đảm bảo rằng các style của component vẫn được kết hợp chặt chẽ với JavaScript và JSX của component. Bạn cũng sẽ tạo một component để triển khai component Alert
đó để xem cách các style có thể ảnh hưởng đến con và cách bạn có thể sử dụng các prop để thay đổi style động.
Đến cuối bước này, bạn sẽ tạo một số component sử dụng CSS thuần túy được import trực tiếp vào component.
Xây dựng component Alert
Để bắt đầu, hãy tạo một component Alert
mới. Đầu tiên, tạo thư mục Alert:
mkdir src/components/Alert
Tiếp theo tạo component Alert.js rồi đưa vào nội dung sau:
Lưu file lại và mở component App.js ra rồi update code như sau:
import './App.css';
import Alert from '../Alert/Alert';
function App() {
return (
<div className="wrapper">
<Alert />
</div>
)
}
export default App;
Lưu lại và quay lại trang web ta được:
Tiếp theo ta mở file App.css rồi update code thành như sau:
.wrapper {
padding: 20px;
}
Lưu file lại và quay lại trang web. Khi bạn sử dụng Create React App, webpack sẽ lấy CSS đã import và thêm nó vào thẻ <style> ở đầu tệp được hiển thị trong trình duyệt. Nếu bạn nhìn vào phần tử <head>
trong nguồn trang của mình, bạn sẽ thấy các style:
Điều này có nghĩa là bạn có thể giữ CSS bên cạnh component và nó sẽ được thu thập cùng nhau trong giai đoạn xây dựng. Điều đó cũng có nghĩa là style của bạn có phạm vi toàn cục (global), điều này có thể tạo ra xung đột tên tiềm ẩn. Với phương thức này, mỗi tên lớp sẽ cần phải là duy nhất trên tất cả các component.
Để khám phá vấn đề này, bạn sẽ thực hiện một số thay đổi đối với component Alert
.
Đầu tiên, hãy mở file Alert.js ra sau đó thêm một số code React trong đó có các prop children
, type
và title
:
import PropTypes from 'prop-types';
export default function Alert({ children, title, type }) {
return (
<div>
<h2>{title}</h2>
{children}
</div>
)
}
Alert.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element.isRequired
]),
title: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
}
title
trong đoạn code trên nằm trong thẻ <h2>
, còn children
sẽ cho phép ta hiển thị các component con. Bạn sẽ sớm sử dụng prop type
để thiết lập các thông báo thành công và thông báo lỗi dựa trên hệ thống kiểu PropTypes.
Lưu file lại. Tiếp theo hãy cập nhật component Alert
trong App
để sử dụng các prop mới.
Mở App.js
ra, sau đó thiết lập một thông báo cho người dùng biết rằng việc thêm item vào giỏ hàng là không thành công:
import './App.css';
import Alert from '../Alert/Alert';
function App() {
return (
<div className="wrapper">
<Alert title="Items Not Added" type="error">
<div>Your items are out of stock.</div>
</Alert>
</div>
)
}
export default App;
Trong đoạn code trên bạn đã cập nhật title
và children
với một thông báo không thành công, sau đó thêm một type
chứa error
.
Lưu file và quay lại trang web bạn sẽ thấy kết quả:
Bước tiếp theo là tạo style cho component bằng CSS.
Thêm CSS vào component Alert
Vì component Alert
sẽ có nhiệm vụ hiển thị ra lỗi, nên bạn sẽ thêm đường viền và đặt màu của đường viền thành màu đỏ. Bạn cũng sẽ cung cấp cho thẻ <h2>
cùng một màu. Nhưng điều này sẽ gặp phải một vấn đề: Bạn không thể sử dụng tên wrapper
bên ngoài <div>
trong component Alert
của mình, bởi vì tên đó đã được component App
sử dụng.
Xung đột tên class không phải là một vấn đề mới trong CSS và đã có một số nỗ lực để giải quyết nó bằng cách sử dụng các quy ước đặt tên như BEM. Nhưng các quy ước đặt tên có thể trở nên dài dòng và đôi khi vẫn có thể dẫn đến xung đột trong các dự án có một số lượng lớn các component.
Thay vì sử dụng một bộ quy tắc cụ thể được phân tách bằng quy ước đặt tên, trong hướng dẫn này, bạn sẽ đặt tiền tố class wrapper
với tên của component. Tên class mới của bạn sẽ là alert-wrapper
. Ngoài ra, bạn sẽ thêm cảnh báo type
dưới dạng một class.
Mở file Alert.js
ra và thêm những phần được đánh dấu như sau:
import PropTypes from 'prop-types';
import './Alert.css';
...
export default function Alert({ children, type, title }) {
return(
<div className={`alert-wrapper ${type}`}>
<h2>{title}</h2>
{children}
</div>
)
}
...
Trong trường hợp này, bạn đang kết hợp alert-wrapper
và biến type
thành một chuỗi duy nhất bằng cách sử dụng một ký tự mẫu.
Lưu file lại.. Bây giờ bạn có một tên class duy nhất có thể thay đổi động dựa trên prop. JSX trong mã này sẽ phân giải thành một div
với các tên class là alert-wrapper
và error
. Kết quả sẽ là: <div class="alert-wrapper error">
.
Bây giờ ta thêm các style. Đầu tiên, ta tạo file Alert.css
rồi đưa vào đoạn code sau:
.alert-wrapper {
padding: 15px;
margin-bottom: 15px;
}
.alert-wrapper h2 {
margin: 0 0 10px 0;
}
.alert-wrapper.success {
border: #6DA06F solid 1px;
}
.success h2 {
color: #6DA06F;
}
.alert-wrapper.error {
border: #F56260 solid 1px;
}
.error h2 {
color: #F56260;
}
Ở đoạn code CSS trên sẽ thêm một số margin và padding vào alert-wrapper
. Sau đó, nó thêm một đường viền có màu đỏ cho class error
bằng cách sử dụng mã màu hệ thập lục là #F56260
và màu xanh lá cây (#6DA06F
) cho class success
. Nó cũng cập nhật màu của <h2>
thành đỏ hoặc xanh lá cây tùy thuộc vào cha.
Lưu file lại và quay lại trang web ta được kết quả như sau:
Bây giờ bạn đã có một component Alert
được tạo style, bạn có thể tạo một component mới hiển thị danh sách các mục trong component Alert
đó.
Tạo component thông báo thành công
Đầu tiên, tạo một thư mục CartSuccess
cho component mới:
mkdir src/components/CartSuccess
Tiếp theo bạn tạo file CartSuccess.js
rồi mở ra và đưa vào đoạn code sau:
import Alert from '../Alert/Alert';
import './CartSuccess.css';
export default function CartSuccess() {
return(
<Alert title="Added to Cart" type="success">
<div className="cart-success-wrapper">
<h2>
You have added 3 items:
</h2>
<div className="item">
<div>Bananas</div>
<div>Quantity: 2</div>
</div>
<div className="item">
<div>Lettuce</div>
<div>Quantity: 1</div>
</div>
</div>
</Alert>
)
}
Lưu ý cách bạn cần để tạo một tên class duy nhất ở đây là đặt thành cart-success-wrapper
. Lưu file lại.
Tiếp theo, thêm một số CSS vào thông báo tùy chỉnh. Tạo file CartSuccess.css
rồi mở ra. Thêm một display: flex vào wrapper. Bạn sẽ muốn hầu hết các mục được bao bọc, ngoại trừ phần tử <h2>
. Bạn đưa code CSS sau vào:
.cart-success-wrapper {
border-top: black solid 1px;
display: flex;
flex-wrap: wrap;
}
.cart-success-wrapper h2 {
width: 100%;
}
.item {
margin-right: 20px;
}
Ở đây, bạn đã cung cấp cho <h2>
chiều rộng là 100%
. Ngoài việc làm linh hoạt phần tử, bạn cũng đã thêm một đường viền nhỏ vào đầu thông báo và thêm một lề vào class item
để cung cấp một số khoảng cách giữa các mục.
Lưu file lại.
Bây giờ bạn đã có một component được tạo style, hãy thêm nó vào component App
của bạn.
Mở file App.js
ra và update theo những đánh dấu như sau:
import './App.css';
import Alert from '../Alert/Alert';
import CartSuccess from '../CartSuccess/CartSuccess';
function App() {
return(
<div className="wrapper">
<Alert title="Items Not Added" type="error">
<div>Your items are out of stock.</div>
</Alert>
<CartSuccess />
</div>
)
}
export default App;
Lưu file và quay lại trang web bạn sẽ thấy kết quả:
Ở đây ta thấy rằng đã hiển thị màu mới và thông báo như dự định, nhưng component lồng nhau nhận được một số style không mong muốn. Quy tắc cho <h2>
trong component Alert
đang được áp dụng cho thẻ <h2>
lồng trong prop children
.
Các CSS không mong muốn cho children là một vấn đề phổ biến với CSS. Tuy nhiên, vì React cung cấp cho bạn cơ hội để nhóm và chia sẻ các component giữa các dự án, nên bạn có nhiều khả năng hơn để các kiểu vô tình chuyển xuống các component con.
Để khắc phục điều này bằng CSS thuần túy, hãy tạo quy tắc <h2>
cho compnent Alert
cụ thể hơn một chút.
Mở file Alert.css
ra và thay đổi các quy tắc để <h2>
chỉ áp dụng cho các class con trực tiếp thay vì tất cả các class con sử dụng >
:
.alert-wrapper {
padding: 15px;
margin-bottom: 15px;
}
.alert-wrapper > h2 {
margin: 0 0 10px 0;
}
.alert-wrapper.success {
border: #6da06f solid 1px;
}
.success > h2 {
color: #6da06f;
}
.alert-wrapper.error {
border: #f56260 solid 1px;
}
.error > h2 {
color: #f56260;
}
Lưu file lại và quay lại trang web ta sẽ được kết quả:
Bây giờ các style cho component Alert
sẽ chỉ ảnh hưởng đến các con trực tiếp và sẽ không áp dụng cho các phần tử con hay component con khác. Phương pháp này hoạt động tốt trong trường hợp này, nhưng trong trường hợp các component phức tạp hơn, có thể khó viết các quy tắc áp dụng cho tất cả các trường hợp mà không bị rò rỉ bên ngoài component.
Như vậy trong bước này, bạn đã tạo style cho một component bằng cách sử dụng CSS được import trực tiếp vào một component. Tạo style các phần tử React với CSS chuẩn là một cách nhanh chóng để tạo các component với các style được liên kết bằng cách sử dụng các tệp CSS chuẩn. Tính dễ sử dụng khiến nó trở thành bước đầu tiên tốt khi bạn đang làm việc với các dự án mới hoặc nhỏ, nhưng khi các dự án phát triển, nó có thể gây ra vấn đề.
Khi bạn xây dựng các component, bạn gặp phải hai vấn đề về style phổ biến: xung đột tên class và áp dụng style không mong muốn. Bạn có thể giải quyết chúng bằng CSS chuẩn, nhưng có những cách tiếp cận tạo style khác cung cấp cho bạn các công cụ để xử lý những vấn đề này theo chương trình thay vì với các quy ước đặt tên. Trong bước tiếp theo, bạn sẽ khám phá việc giải quyết những vấn đề này với các đối tượng style.
Bước 3 - Tạo style với các đối tượng style
Trong bước này, bạn sẽ tạo kiểu cho các component của mình bằng cách sử dụng các đối tượng style, là các đối tượng JavaScript sử dụng thuộc tính CSS làm khóa. Khi bạn làm việc trên các component của mình, bạn sẽ cập nhật các khóa để khớp với cú pháp JavaScript và tìm hiểu cách đặt động các thuộc tính kiểu dựa trên các prop component.
CSS riêng biệt là cách phổ biến nhất để tạo kiểu HTML. Phương pháp này nhanh chóng và các trình duyệt có hiệu quả trong việc áp dụng các kiểu một cách nhanh chóng và nhất quán. Nhưng đây không phải là lựa chọn duy nhất để tạo kiểu HTML. Trong HTML chuẩn, bạn có thể đặt các kiểu nội tuyến trực tiếp trên một phần tử bằng cách sử dụng thuộc tính style với một chuỗi chứa các kiểu bạn muốn áp dụng.
Một trong những cách sử dụng tốt nhất của đối tượng kiểu là để tính toán kiểu động. Điều này đặc biệt hữu ích nếu bạn cần biết vị trí hiện tại của phần tử, vì điều này không được xác định cho đến khi các phần tử được hiển thị và do đó chỉ có thể được xử lý động.
Viết chuỗi kiểu thủ công rất khó thực hiện và có thể gây ra lỗi. Thiếu màu hoặc dấu chấm phẩy sẽ làm đứt toàn bộ chuỗi. May mắn thay, trong JSX, bạn không bị giới hạn chỉ trong một chuỗi. Thuộc tính style cũng có thể chấp nhận một đối tượng chứa các style. Những tên kiểu này sẽ cần phải được camelCase
thay thế kebab-case
.
Ưu điểm lớn nhất của việc sử dụng các kiểu nội tuyến như thế này là vì bạn đang xây dựng các kiểu bằng JavaScript, bạn có thể đặt động các thuộc tính CSS thay vì đặt động các lớp. Điều này có nghĩa là bạn có thể viết mã mà không cần các lớp CSS, tránh mọi xung đột tên tiềm ẩn và cho phép bạn tính toán các kiểu trong thời gian chạy.
Để sử dụng các đối tượng kiểu, hãy bắt đầu bằng cách cấu trúc lại component App.js
.
Đầu tiên, hãy mở file App.js, loại bỏ lệnh import App.css
, và sau đó tạo ra một đối tượng mà có padding
là 20
và truyền đối tượng vào thẻ <div>
sử dụng thuộc tính style
:
import Alert from '../Alert/Alert';
import CartSuccess from '../CartSuccess/CartSuccess';
function App() {
const wrapper = {
padding: 20
};
return(
<div style={wrapper}>
<Alert title="Items Not Added" type="error">
<div>Your items are out of stock.</div>
</Alert>
<CartSuccess />
</div>
)
}
export default App;
Lưu ý rằng bạn không phải chỉ định pixel làm đơn vị cho
padding
. React sẽ chuyển đổi nó thành một chuỗi pixel theo mặc định. Nếu bạn muốn một đơn vị cụ thể, hãy chuyển nó dưới dạng một chuỗi. Vì vậy, nếu bạn muốn phần đệm là một tỷ lệ phần trăm, chẳng hạn, nó sẽ làpadding: '20%'
.Hầu hết các số sẽ được tự động chuyển đổi thành pixel. Tuy nhiên, vẫn có những trường hợp ngoại lệ. Thuộc tính
line-height
có thể lấy số đơn vị mà không có đơn vị. Nếu bạn muốn sử dụng đơn vị pixel trong trường hợp đó, bạn cần chỉ định pixel dưới dạng chuỗi.
Lưu file lại và quay lại trang web bạn sẽ thấy kết quả không thay đổi.
Tiếp theo, ta tái cấu trúc component CartSuccess
. Đầu tiên, hãy mở file CartSuccess.js ra. Tương tự như App.js
, hãy xóa lệnh import CartSuccess.css
đi và tạo một đối tượng style cho từng mục mà trước đó có một class:
import Alert from '../Alert/Alert';
export default function CartSuccess() {
const styles = {
header: {
width: '100%'
},
item: {
marginRight: 20
},
wrapper: {
borderTop: 'black solid 1px',
display: 'flex',
flexWrap: 'wrap'
}
}
return(
<Alert title="Added to Cart" type="success">
<div style={styles.wrapper}>
<h2 style={styles.header}>
You have added 3 items:
</h2>
<div style={styles.item}>
<div>Bananas</div>
<div>Quantity: 2</div>
</div>
<div style={styles.item}>
<div>Lettuce</div>
<div>Quantity: 1</div>
</div>
</div>
</Alert>
)
}
Trong trường hợp này, bạn đã không tạo nhiều đối tượng riêng biệt; thay vào đó, bạn đã tạo một đối tượng duy nhất có chứa các đối tượng khác. Cần lưu ý rằng bạn cần sử dụng hình thức lạc đà cho các thuộc tính như margin-right
, border-top
và flex-wrap
.
Lưu file lại và quay lại trang web bạn sẽ thấy kết quả không thay đổi.
Vì bạn không sử dụng class, nên bạn không phải lo lắng về bất kỳ xung đột tên nào. Một lợi thế khác của việc tạo kiểu bằng JavaScript là bạn có thể tận dụng bất kỳ cú pháp JavaScript nào như các biến và các ký tự mẫu. Với CSS hiện đại, bạn có thể sử dụng các biến, đây là một cải tiến lớn, nhưng có thể không khả dụng đầy đủ tùy thuộc vào yêu cầu hỗ trợ trình duyệt của bạn. Đặc biệt, chúng không được hỗ trợ trong bất kỳ phiên bản nào của Internet Explorer, mặc dù bạn có thể sử dụng polyfill để thêm hỗ trợ.
Vì các đối tượng style được tạo trong thời gian chạy, chúng dễ dự đoán hơn và có thể sử dụng bất kỳ JavaScript nào được hỗ trợ.
Để xem các đối tượng style có thể giúp ích như thế nào trong trường hợp này, hãy cấu trúc lại Alert.js
để sử dụng các đối tượng style.
Đầu tiên, hãy mở Alert.js
ra. Bên trong Alert.js
ta loại bỏ lệnh import './Alert.css';
và tạo một đối tượng có tên colors
có thuộc tính cho màu lỗi và thuộc tính cho màu thành công. Sau đó, chuyển đổi CSS thành một đối tượng JavaScript bằng cách sử dụng prop type
để đặt động màu:
import PropTypes from 'prop-types';
export default function Alert({ children, type, title }) {
const colors = {
success: '#6da06f',
error: '#f56260',
}
const style = {
heading: {
color: colors[type],
margin: '0 0 10px 0',
},
wrapper: {
border: `${colors[type]} solid 1px`,
marginBottom: 15,
padding: 15,
position: 'relative',
}
}
return(
<div style={style.wrapper}>
<h2 style={style.heading}>{title}</h2>
{children}
</div>
)
}
...
Có một vài thay đổi ở đây. Đầu tiên, bạn sử dụng một khai báo style duy nhất cho wrapper
và sau đó đặt động màu dựa trên style. Bạn không tạo style cho các phần tử <h2>
nói chung, mà thay vào đó bạn đang tạo style cho các phần tử cụ thể này. Vì bạn không áp dụng kiểu cho một loại phần tử nào cả, cho nên không có gì nguy hiểm khi style sẽ áp dụng xuống các phần tử con.
Lưu file lại và quay lại trang web bạn sẽ thấy kết quả không đổi.
Như vây, đối tượng kiểu giải quyết được nhiều vấn đề, nhưng chúng có nhược điểm. Đầu tiên, có một chi phí hiệu suất cho các style nội tuyến. Các trình duyệt được thiết kế để xử lý CSS một cách hiệu quả và các đối tượng tạo style áp dụng style nội tuyến không thể tận dụng những tối ưu hóa này. Vấn đề khác là việc áp dụng style cho các phần tử con với các đối tượng style sẽ khó hơn. Trong trường hợp này, bạn không muốn một style áp dụng cho tất cả các con, nhưng thường là bạn muốn style xếp tầng. Ví dụ: đặt phông chữ tùy chỉnh trên mọi phần tử hoặc áp dụng kích thước tùy chỉnh cho mọi phần tử <h2>
sẽ dễ dàng hơn nếu bạn sử dụng chiến lược tạo kiểu ít cụ thể hơn.
Tuy nhiên, có một điểm trung gian giữa những cách tiếp cận này. Một số thư viện của bên thứ ba được thiết kế để tìm điểm trung gian này. Trong bước tiếp theo, bạn sẽ tạo style bằng cách sử dụng phương pháp kết hợp được gọi là CSS-in-JS bằng cách sử dụng thư viện có tên JSS.
Bước 4 - Tạo style với JSS
Trong bước này, bạn sẽ tạo style cho các đối tượng bằng cách sử dụng thư viện phổ biến là JSS. Bạn sẽ cài đặt thư viện mới và chuyển đổi các đối tượng style của mình thành các đối tượng JSS. Sau đó, bạn sẽ cấu trúc lại mã của mình để sử dụng các tên class được tạo động để ngăn xung đột giữa các tên class giữa các mô-đun. Bạn cũng sẽ xây dựng các đối tượng style JavaScript để đặt style động và sử dụng các thuộc tính lồng nhau để tạo các quy tắc style cụ thể.
JSS là một thư viện để tạo CSS-in-JS. Phương pháp này có nhiều trường hợp sử dụng và tùy chọn khác nhau, nhưng ưu điểm chính trong hướng dẫn này là nó sẽ tạo ra các tên class động để tránh xung đột giữa các component. Bạn cũng sẽ có thể tận dụng cú pháp JavaScript, có nghĩa là bạn sẽ có thể sử dụng các biến và tạo style dựa trên các prop React.
Để bắt đầu, hãy cài đặt JSS thông qua lệnh sau:
npm install react-jss
Gói này sẽ cài đặt một số phụ thuộc, bao gồm một số plugin JSS sẽ cung cấp cho bạn khả năng viết các quy tắc style ngắn gọn.
Bây giờ thư viện đã được cài đặt, hãy chuyển đổi App.js
để sử dụng JSS.
Đầu tiên, hãy mở file App.js
ra. Có hai bước để sử dụng JSS. Đầu tiên, bạn phải import một hàm để tạo một hook tùy chỉnh. Hooks là các hàm mà React sẽ chạy trên mọi component render. Với JSS, bạn phải tạo một hook bằng cách truyền các định nghĩa style ra bên ngoài component. Điều này sẽ ngăn mã chạy trên mỗi lần hiển thị lại; vì các định nghĩa style là tĩnh, không có lý do gì để chạy mã thêm một lần nữa.
Tạo hook và đối tượng style bằng cách thực hiện các thay đổi được đánh dấu như dưới đây:
import { createUseStyles } from 'react-jss';
import Alert from '../Alert/Alert';
import CartSuccess from '../CartSuccess/CartSuccess';
const useStyles = createUseStyles({
wrapper: {
padding: 20,
}
});
function App() {
return(
<div style={wrapper}>
<Alert title="Items Not Added" type="error">
<div>Your items are out of stock.</div>
</Alert>
<CartSuccess />
</div>
)
}
export default App;
Lưu ý trong trường hợp này rằng đối tượng style của bạn chứa một đối tượng khác được gọi wrapper
, chứa các style sử dụng cùng một định dạng lạc đà. Tên của đối tượng— wrapper
—là cơ sở để tạo tên lớp động.
Sau khi bạn tạo hook, bạn sử dụng nó bằng cách thực thi hàm bên trong component. Điều này đăng ký hook và tạo kiểu động. Thực hiện thay đổi được đánh dấu như sau:
import { createUseStyles } from 'react-jss'
import Alert from '../Alert/Alert';
import CartSuccess from '../CartSuccess/CartSuccess';
const useStyles = createUseStyles({
wrapper: {
padding: 20,
}
});
function App() {
const classes = useStyles()
return(
<div className={classes.wrapper}>
<Alert title="Items Not Added" type="error">
<div>Your items are out of stock.</div>
</Alert>
<CartSuccess />
</div>
)
}
export default App;
Trong đoạn mã trên, bạn gọi hàm và gán kết quả cho một biến được gọi classes
. Biến mới classes
sẽ là một đối tượng chứa tên class động. Sau đó, bạn áp dụng tên class thích hợp cho phần tử của mình bằng cách sử dụng cùng tên mà bạn đã định nghĩa trên đối tượng. Ở đây bạn đã sử dụng classes.wrapper
.
Lưu lại và quay lại trang web bạn sẽ thấy nội dung không thay đổi. Nhưng nếu bạn nhìn vào console thì bạn sẽ thấy rằng tên class không hoàn toàn khớp với tên bạn đã định nghĩa trong đối tượng của mình:
Trong trường hợp trên thì tên class là wrapper-0-2-1
, nhưng trên máy tính của bạn thì tên class có thể khác. Bạn cũng thấy ở trên rằng các style được chuyển đổi từ một đối tượng sang CSS và được đặt trong một thẻ <style>
. Điều này là đối lập với các style nội tuyến, nơi không được chuyển đổi sang CSS và không có bất kỳ tên class nào.
JSS tạo động các tên class để chúng không xung đột với các tên tương tự trong các tệp khác. Để chứng mình điều này ta hãy cấu trúc lại CartSuccess.js
để sử dụng kiểu JSS.
Mở file CartSuccess.js ra. Bên trong file ta tạo một hook tùy chỉnh bằng cách sử dụng createUseStyles
. Thay vì áp dụng mỗi class cho một phần tử <h2>
, bạn sẽ tạo một quy tắc cho các phần tử <h2>
bên trong một wrapper. Để làm điều đó với CSS thuần túy, bạn thêm khoảng trắng giữa class và phần tử— .wrapper h2
. Điều này sẽ áp dụng style cho tất cả các phần tử <h2>
là con cháu chắt của của class .wrapper
.
Với JSS, bạn có thể tạo một quy tắc tương tự bằng cách tạo một đối tượng khác bên trong phần tử chứa. Để liên kết chúng với nhau, hãy bắt đầu tên đối tượng bằng ký hiệu &
:
import { createUseStyles } from 'react-jss';
import Alert from '../Alert/Alert';
const useStyles = createUseStyles({
item: {
marginRight: 20
},
wrapper: {
borderTop: 'black solid 1px',
display: 'flex',
flexWrap: 'wrap',
'& h2': {
width: '100%'
}
}
})
export default function CartSuccess() {
const classes = useStyles();
return(
<Alert title="Added to Cart" type="success">
<div className={classes.wrapper}>
<h2>
You have added 3 items:
</h2>
<div className={classes.item}>
<div>Bananas</div>
<div>Quantity: 2</div>
</div>
<div className={classes.item}>
<div>Lettuce</div>
<div>Quantity: 1</div>
</div>
</div>
</Alert>
)
}
Ngoài việc tạo quy tắc cho wrapper, bạn cũng tạo quy tắc cho item
. Sau khi tạo hook tùy chỉnh, bạn đã truyền tên lớp tùy chỉnh cho thuộc tính className
.
Lưu file lại. Lưu ý rằng bạn đang sử dụng cùng một tên— wrapper
—trong cả component này và component App
. Nhưng khi trình duyệt tải lại, sẽ không có xung đột về tên; mọi thứ sẽ chính xác. Nếu bạn kiểm tra các phần tử trong trình duyệt của mình, bạn sẽ thấy rằng mặc dù chúng bắt đầu bằng cùng một tên, nhưng chúng đều có một class duy nhất:
Trong hình trên, class cho component bên ngoài là wrapper-0-2-1
được tạo ra trong component App
. Còn class cho CartSuccess
là wrapper-0-2-3
. Tên component trong trang web của bạn có thể hơi khác một chút, nhưng chúng sẽ là duy nhất.
Trong một số trường hợp, bạn có thể cần phải tạo một bộ chọn cụ thể để ghi đè các style khác. Ví dụ: giả sử bạn chỉ muốn item
áp dụng style khi phần tử là phần tử con của class wrapper
. Để làm điều này, đầu tiên hãy tạo class trên đối tượng không có thuộc tính nào. Sau đó, bên trong class wrapper
bạn tham chiếu lớp mới bằng ký hiệu $
:
import { createUseStyles } from 'react-jss'
import Alert from '../Alert/Alert';
const useStyles = createUseStyles({
item: {},
wrapper: {
borderTop: 'black solid 1px',
display: 'flex',
flexWrap: 'wrap',
'& h2': {
width: '100%'
},
'& $item': {
marginRight: 20
}
}
})
export default function CartSuccess() {
const classes = useStyles()
return(
<Alert title="Added to Cart" type="success">
<div className={classes.wrapper}>
<h2>
You have added 3 items:
</h2>
<div className={classes.item}>
<div>Bananas</div>
<div>Quantity: 2</div>
</div>
<div className={classes.item}>
<div>Lettuce</div>
<div>Quantity: 1</div>
</div>
</div>
</Alert>
)
}
Lưu file lại và quay lại trang web bạn sẽ thấy trang web không thay đổi nội dung, nhưng CSS cho item
sẽ được áp dụng cụ thể hơn cho các mục trong component wrappler.
JSS cung cấp cho bạn khả năng tạo các quy tắc với cùng mức độ trọng tâm mà bạn tạo với CSS thông thường, nhưng sẽ làm như vậy trong khi tạo các tên class duy nhất để đảm bảo không xung đột.
Một lợi thế cuối cùng của JSS là bạn có khả năng sử dụng các biến và các tính năng ngôn ngữ JavaScript khác. Vì bạn đang sử dụng react-jss
, bạn có thể truyền các prop cho đối tượng style để tạo style động. Để kiểm tra điều này, hãy cấu trúc lại component Alert.js
để sử dụng các prop và biến để tạo thuộc tính động.
Đầu tiên, hãy mở file Alert.js. Tạo một đối tượng style giống như bạn đã làm trong mã được cấu trúc lại ở trên. Đảm bảo di chuyển đối tượng định nghĩa màu sắc bên ngoài hàm thành phần để nó nằm trong cùng phạm vi với hàm createUseStyles
:
import PropTypes from 'prop-types';
import { createUseStyles } from 'react-jss';
const colors = {
success: '#6da06f',
error: '#f56260',
};
const useStyles = createUseStyles({
wrapper: {
border: ({ type }) => `${colors[type]} solid 1px`,
marginBottom: 15,
padding: 15,
position: 'relative',
'& h2': {
color: ({ type }) => colors[type],
margin: [0, 0, 10, 0],
}
}
});
export default function Alert({ children, type, title }) {
const classes = useStyles({ type })
return(
<div className={classes.wrapper}>
<h2>{title}</h2>
{children}
</div>
)
}
...
Trong đoạn code trên, để truyền prop, bạn đặt quy tắc style thành một hàm. Hàm chấp nhận các prop như một đối số, sau đó trả về một quy tắc. Để tạo ra một border động, bạn thêm border
như tên thuộc tính và một hàm mũi tên chứa type
và trả về một chuỗi: ({ type }) => `${colors[type]} solid 1px`,
. Sau đó, sau khi bạn tạo hook của mình, bạn truyền prop mà bạn muốn tham chiếu khi tạo đối tượng class. Bạn định kiểu cho thẻ <h2>
theo phần tử thay vì tạo một class cụ thể. Bạn cũng truyền một mảng giá trị margin
thay vì một chuỗi.
Lưu file lại. Lưu ý rằng bạn không cần phải truyền tất cả các prop vào hàm. Trong trường hợp này, bạn chỉ muốn sử dụng type
, vì vậy bạn chỉ cần truyền type là đủ. Tuy nhiên, bạn có thể truyền nhiều hơn hoặc thậm chí truyền các prop không xác định bằng cách sử dụng toán tử ...props để thu thập các prop và sau đó truyền chúng dưới dạng một nhóm. Bạn cần phải truyền nó như một đối tượng; tuy nhiên, vì đó là cách tiêu chuẩn để truyền prop, nó sẽ giúp việc mở rộng đối số dễ dàng hơn trong tương lai.
Bây giờ quay lại trang web bạn sẽ thấy các màu chính xác, nhưng sẽ có một vấn đề nhỏ: màu success màu xanh hiện đang áp dụng cho phần từ <h2>
trong CartSuccess
:
JSS giải quyết được nhiều vấn đề, nhưng nó vẫn có thể tạo ra CSS chuẩn. Điều đó có nghĩa là các style có thể áp dụng cho các phần tử con nếu bạn không cẩn thận. Để khắc phục điều này, hãy thêm biểu tượng >
để làm cho CSS chỉ áp dụng cho các phần tử con trực tiếp:
...
const useStyles = createUseStyles({
wrapper: {
border: ({ type }) => `${colors[type]} solid 1px`,
marginBottom: 15,
padding: 15,
position: 'relative',
'& > h2': {
color: ({ type }) => colors[type],
margin: [0, 0, 10, 0],
}
}
});
export default function Alert({ children, type, title }) {
...
}
...
Lưu file lại và quay lại trang web bạn sẽ thấy một kết quả hoàn toàn chính xác.
Còn nhiều điều khác về JSS ngoài những gì được đề cập trong bài hướng dẫn này. Một lợi thế quan trọng mà chúng ta chưa đề cập đến là theming. JSS cung cấp cho bạn khả năng tạo style dựa trên các đối tượng theme được định nghĩa trước. Điều đó có nghĩa là thay vì tạo màu đỏ từ một giá trị được mã hóa cứng, bạn có thể tạo màu alert
cho đường viền alert, có thể sẽ là màu đỏ, nhưng có thể khác tùy thuộc vào định nghĩa theme. Điều này rất hữu ích khi tạo các sản phẩm nhãn trắng hoặc tạo các component có thể tái sử dụng cần hoạt động trong các dự án.
Trong bước này, bạn đã tạo kiểu cho các component bằng cách sử dụng thư viện của bên thứ ba được gọi là react-jss
. Bạn cũng đã tạo đối tượng style và sử dụng JSS để chuyển đổi các đối tượng đó thành các class động để tránh xung đột với các component khác. Sử dụng phương pháp này, bạn có thể sử dụng lại các tên class đơn giản một cách an toàn mà không phải lo lắng về xung đột sau này trong mã. Cuối cùng, bạn đã học cách tạo style bằng cách sử dụng các hàm và prop để xây dựng style động tham chiếu đến các prop component.
Phần kết luận
Trong suốt bài hướng dẫn này, bạn đã phát triển một số component có thể tái sử dụng sử dụng các kỹ thuật style khác nhau. Bạn đã học cách các đối tượng style và JSS tạo các đối tượng bằng cách sử dụng các tên sao chép chặt chẽ các thuộc tính CSS chuẩn và đã tạo các component có thể đặt style động dựa trên các thuộc tính đến. Bạn cũng đã học cách các cách tiếp cận khác nhau cung cấp các tùy chọn khác nhau để xử lý xung đột tên và khả năng tái sử dụng.
Như với hầu hết các kỹ thuật React, sẽ không có giải pháp tốt nhất. Thay vào đó, bạn có thể chọn tùy chọn tạo style phù hợp nhất cho dự án của mình. Với những tùy chọn này trong tay, bạn có thể bắt đầu với một cái gì đó đơn giản và tái cấu trúc khi dự án phát triển hoặc các yêu cầu thay đổi, trong khi vẫn tự tin rằng các component của bạn sẽ tiếp tục đáp ứng các mục tiêu style của bạn.