Hướng dẫn định kiểu (style) cho React Component
- 29-09-2022
- Toanngo92
- 0 Comments
Trong bài viết này, chúng ta sẽ tìm hiểu 3 giải pháp khác nhau để định kiểu cho các React component:
- Sử dụng CSS thuần
- CSS inline bằng javacript-style objects
- Thư viện JSS để 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 tham khảo trực tiếp các props 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 component style cụ thể của mình gần với component, giúp các thành phần 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 tới nhau.
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, bạn có thể sử dụng import stylesheet như các ví dụ từ các bài trước. Nếu bạn muốn tạo định kiểu được tích hợp với component, bạn có thể sử dụng các đối tượng inline sử dụng tên thuộc tính CSS làm key và tên thuộc tính làm value. 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 giải pháp này, mình sẽ tạo một dự án mới, và chúng ta sẽ xây dựng một Component có tên Notify sẽ hiển thị kiểu thành công hoặc lỗi tùy thuộc vào prop. Component Notify sẽ nhận bất kỳ số lượng child nào. Có nghĩa là chúng ta sẽ cần phải thận trọng với các xung đột về style, vì chúng ta không có cách nào biết được các style mà các child component sẽ có. Sau khi tạo Component Notify, chúng ta sẽ cấu trúc lại nó bằng cách sử dụng từng tùy chọn để style giúp chúng ta có thể thấy sự giống và khác nhau giữa các phương pháp.
Bắt đầu khởi tạo dự án mới và cài đặt PropTypes bằng dòng lệnh:
npx create-react-app reactstyledemo
cd reactstyledemo
npm install --save prop-types
yarn start
// npm start
Mục lục
Định kiểu Component bằng CSS tiêu chuẩn
Nội dung file Component/Notify/Notify.js
import React from "react";
const NotifyComp = () => {
return (<div>Notify</div>)
}
export default NotifyComp
Sửa nội dung file App.js
import './App.css';
import NotifyComp from './Component/Notify/NotifyComp';
function App() {
return (
<div className="App">
<NotifyComp></NotifyComp>
</div>
);
}
export default App;
Sửa file App.css
.App{
padding: 15px;
}
Cấu trúc thư mục và output:
Nhìn hình trên ta nhận diện được rằng khi bạn sử dụng Create React App, webpack sẽ lấy CSS đã import và thêm nó vào thẻ <style> bên trong thẻ <head/> hiển thị trong trình duyệt.
Đ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 build ứng dụng. Điều đó cũng có nghĩa là style của bạn có phạm vi toàn cục, đ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 class 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 Component/Notify/Notify.js như sau:
import React from "react";
import PropTypes from 'prop-types';
const NotifyComp = ({ children, title, type }) => {
return (<div>
<h3>{title}</h3>
{children}
</div>)
}
export default NotifyComp
NotifyComp.propTypes = {
title: PropTypes.string,
type: PropTypes.string.isRequired,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element.isRequired
])
};
NotifyComp.defaultProps = {
title: '',
type: '',
children: null
}
Sửa file App.js
import './App.css';
import NotifyComp from './Component/Notify/NotifyComp';
function App() {
return (
<div className="App">
<NotifyComp title="Product do not added to cart" type='error'>
<div>Your items are out of stock</div>
</NotifyComp>
</div>
);
}
export default App;
Output:
Giờ mình muốn hiển thị box này cho giống một thông báo lỗi, với màu đỏ là màu chính, nếu xử lý theo cách đơn giản, chúng ta chỉ cần đặt class mới cho thẻ <div> của NotifyComponent là xong, chúng ta đi tới ví dụ tiếp theo:
Sửa file App.js
import './App.css';
import NotifyComp from './Component/Notify/NotifyComp';
function App() {
return (
<div className="App">
<NotifyComp title="Product do not added to cart" type='error'>
<div>Your items are out of stock</div>
</NotifyComp>
</div>
);
}
export default App;
sửa file Component/Notify/Notify.js như sau:
import React from "react";
import PropTypes from 'prop-types';
import './NotifyComp.css'
const NotifyComp = ({ children, title, type }) => {
return (<div className={`notify-wrapper ${type}`}>
<h3>{title}</h3>
{children}
</div>)
}
export default NotifyComp
NotifyComp.propTypes = {
title: PropTypes.string,
type: PropTypes.string.isRequired,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element.isRequired
])
};
NotifyComp.defaultProps = {
title: '',
type: '',
children: null
}
Tạo file Component/Notify/NotifyComp.css như sau:
.notify-wrapper{
border: 1px solid;
padding: 15px;
margin-bottom: 15px;
}
.notify-wrapper.error{
border-color: red;
}
.notify-wrapper h3{
font-weight: bold;
margin: 0px;
}
.notify-wrapper.error h3{
color: red;
}
.notify-wrapper.success{
border-color: green;
}
.notify-wrapper.success h3{
color: green;
}
Cấu trúc thư mục và output:
Từ notify component, chúng ta sẽ triển khai ra các component Component/Notify/SuccessComp.js như sau:
import React from "react";
import PropTypes from 'prop-types';
import NotifyComp from "./NotifyComp";
import './SuccessComp.css'
const SuccessComp = () => {
return (<NotifyComp title='Product Added to cart successfully' type="success">
<div className="success-wrapper">
<h4>You have add item:</h4>
<div className="products-wrapper">
<div className="product">
<h5>My first product</h5>
<p>Quantity: 2</p>
</div>
<div className="product">
<h5>My second product</h5>
<p>Quantity: 1</p>
</div>
</div>
</div>
</NotifyComp>)
}
export default SuccessComp
SuccessComp.propTypes = {
}
Tạo file Component/Notify/SuccessComp.css như sau:
.success-wrapper .products-wrapper h5{
color: brown;
margin: 0px;
}
Sửa file App.js:
import './App.css';
import NotifyComp from './Component/Notify/NotifyComp';
import SuccessComp from './Component/Notify/SuccessComp';
function App() {
return (
<div className="App">
<NotifyComp title="Product do not added to cart" type='error'>
<div>Your items are out of stock</div>
</NotifyComp>
<SuccessComp/>
</div>
);
}
export default App;
Cấu trúc dự án và output:
Các định kiểu không mong muốn đối với các bộ chọn con 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 để đóng gói và chia sẻ các component trong các dự án, bạn có nhiều cách hơn để định kiểu cho các thành phần con.
Định kiểu cho Component bằng Style Objects
Trong hướng dẫn này, chúng ta sẽ tiếp cận cách định kiểu cho các component bằng cách sử dụng các style objects (đối tượng định kiểu), là các đối tượng JavaScript sử dụng thuộc tính CSS làm key. Khi làm việc trên các component, bạn sẽ cập nhật các key để 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 component props.
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 tiêu chuẩn, bạn có thể đặt các style inline 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 style objects là để tính toán định 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 component, 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 định 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 lỗi toàn bộ CSS. May mắn là, 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 là camelCase thay vì 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 style bằng JavaScript, bạn có thể đặt động các thuộc tính CSS thay vì đặt động các class. Điều này có nghĩa là bạn có thể viết mã mà không cần các class CSS, tránh mọi xung đột tên tiềm ẩn và cho phép bạn tính toán các style trong môi trường runtime.
Ví dụ nhanh cho cách định kiểu này bằng việc sửa file App.js:
import './App.css';
import NotifyComp from './Component/Notify/NotifyComp';
import SuccessComp from './Component/Notify/SuccessComp';
function App() {
const App = {
padding: 30
}
return (
<div style={App}>
<NotifyComp title="Product do not added to cart" type='error'>
<div>Your items are out of stock</div>
</NotifyComp>
<SuccessComp/>
</div>
);
}
export default App;
Lưu ý: 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 padding 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. Có ngoại lệ, tuy nhiên. Chiều cao dòng thuộc tính có thể là số đơn giản 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.
Tiếp tục, sử dụng cách này để định kiểu cho Component/Notify/SuccessComp.js như sau:
import React from "react";
import PropTypes from 'prop-types';
import NotifyComp from "./NotifyComp";
// import './SuccessComp.css'
const SuccessComp = () => {
const styles = {
header: {
width: '100%',
color: 'orange'
},
productTitle: {
borderBottom: '1px solid green'
}
}
return (<NotifyComp title='Product Added to cart successfully' type="success">
<div className="success-wrapper">
<h4 style={styles.header}>You have add item:</h4>
<div className="products-wrapper">
<div className="product">
<h5 style={styles.productTitle}>My first product</h5>
<p>Quantity: 2</p>
</div>
<div className="product">
<h5 style={styles.productTitle}>My second product</h5>
<p>Quantity: 1</p>
</div>
</div>
</div>
</NotifyComp>)
}
export default SuccessComp
SuccessComp.propTypes = {
}
Vì không sử dụng class, 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 template literals (kí tự chuỗi nằm trong dấu nháy “). 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, 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 kiểu đượ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ợ.
Để tiếp tục chứng minh các objects style có thể hỗ trợ như thế nào trong trường hợp này, hãy cấu trúc lại Component/Notify/SuccessComp.js để sử dụng các objects styles như sau:
import React from "react";
import PropTypes from 'prop-types';
//import './NotifyComp.css'
const NotifyComp = ({ children, title, type }) => {
const color = {
success: 'green',
error: 'red',
}
const styles = {
heading: {
fontWeight: 'bold',
margin: 0,
color: color[type]
},
notifyWrapper: {
border: `1px solid ${color[type]}`,
padding: 15,
marginBottom: 15
}
}
return (<div style={styles.notifyWrapper}>
<h3 style={styles.heading}>{title}</h3>
{children}
</div>)
}
export default NotifyComp
NotifyComp.propTypes = {
title: PropTypes.string,
type: PropTypes.string.isRequired,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element.isRequired
])
};
NotifyComp.defaultProps = {
title: '',
type: '',
children: null
}
Với ví dụ trên, output vẫn không thay đổi nhưng chúng ta không cần import file CSS vào để sử dụng nữa mà hoàn toàn sử dụng javascript để định kiểu, và cú pháp cũng ngắn gọn hơn.
Objects style giúp giải quyết nhiều vấn đề, nhưng chúng có nhược điểm.Đầu tiên là về vấn đề hiệu suất. 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 kiểu áp dụng kiểu nội tuyến (inline) 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 kiểu cho các phần tử con với các object style sẽ khó hơn. Trong trường hợp này, bạn không muốn một kiểu áp dụng chochold, nhưng thường là bạn muốn kiểu xếp tầng. Ví dụ: đặt họ 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ử 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 kiểu bằng cách sử dụng phương pháp kết hợp có tên là CSS-in-JS bằng cách sử dụng thư viện có tên JSS.
Định kiểu bằng JSS
Trong hướng dẫn này, chúng ta sẽ tạo object style bằng cách sử dụng thư viện JSS. Chúng ta sẽ cần cài đặt thư viện mới và chuyển đổi các object style của mình thành các object JSS. Sau đó, cấu trúc lại mã nguồn để sử dụng các tên class được tạo động để ngăn xung đột tên class giữa các module. Bạn cũng sẽ xây dựng các object style JavaScript để định kiểu động và sử dụng các thuộc tính lồng nhau để tạo các quy tắc định kiểu 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 lớp độ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à template literals dựa trên các React props.
Để bắt đầu, tiến hành cài đặt thư viện JSS. Xem trang offical của thư viện tại đây: JSS (cssinjs.org)
npm install react-jss
Sau khi cài đặt thành công, chúng ta bắt đầu sử dụng. Có hai bước để sử dụng JSS. Đầu tiên, bạn phải nhập 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 thành phần render. Với JSS, bạn phải tạo một hook bằng cách chuyển các định kiểu vào bên ngoài component. Điều này sẽ ngăn không cho mã chạy trên mỗi lần hiển thị lại; vì các định nghĩa kiểu là tĩnh, không có lý do gì để chạy mã nhiều lần nữa.
import './App.css';
import NotifyComp from './Component/Notify/NotifyComp';
import SuccessComp from './Component/Notify/SuccessComp';
import { createUseStyles } from 'react-jss';
const useStyles = createUseStyles({
App: {
padding: 30
}
})
function App() {
const classes = useStyles()
return (
<div className={classes.App}>
<NotifyComp title="Product do not added to cart" type='error'>
<div>Your items are out of stock</div>
</NotifyComp>
<SuccessComp/>
</div>
);
}
export default App;
Output:
Chúng ta có thể thấy, JSS đã tự tạo ra một class duy nhất, và sẽ đảm bảo không xung đột với bất kì class nào trong dự án.
Giải thích luồng chạy:
- Import {createUseStyles} từ thư viện vừa cài đặt
- Sử dụng createUseStyles để tạo một object định kiểu JSS
- Bên trong App() functional, khai báo biến classes và sử dụng hook useStyles() để lấy giá trị object class
- Đưa thuộc tính App của object classes vào markup để định kiểu
Tiếp tục sửa file Component/Notify/SuccessComp.js
import React from "react";
import PropTypes from 'prop-types';
import NotifyComp from "./NotifyComp";
import { createUseStyles } from 'react-jss';
const useStyles = createUseStyles({
header: {
width: '100%',
color: 'orange'
},
productTitle : {
borderBottom: '1px solid green'
}
})
const SuccessComp = () => {
const classes = useStyles()
return (<NotifyComp title='Product Added to cart successfully' type="success">
<div className="success-wrapper">
<h4 className={classes.header}>You have add item:</h4>
<div>
<div className="product">
<h5 className={classes.productTitle}>My first product</h5>
<p>Quantity: 2</p>
</div>
<div className="product">
<h5 className={classes.productTitle}>My second product</h5>
<p>Quantity: 1</p>
</div>
</div>
</div>
</NotifyComp>)
}
export default SuccessComp
SuccessComp.propTypes = {
}
Output không thay đổi, tuy nhiên code khá đẹp và dễ đọc, tốc độ viết có thể nhanh hơn CSS tiêu chuẩn nếu quen dùng.
Tuy vậy, xét tình huống muốn đặt class cho product và css cho các thẻ bên trong hướng xử lý thế nào ? JSS cung cấp giải pháp để xử lý vấn đề này. Đầu tiên, hãy đặt class trong thẻ wrapper, tiếp theo sử dụng ký tự & để biểu diễn chính nó (tương tự cú pháp scss), > vẫn sử dụng để chỉ định tới phần tử cấp con gần nhất, và $ để tham chiếu tới các class hoặc tham chiếu tới thẳng thẻ con bên trong theo cấu trúc như sau:
import React from "react";
import PropTypes from 'prop-types';
import NotifyComp from "./NotifyComp";
import { createUseStyles } from 'react-jss';
const useStyles = createUseStyles({
productTitle: {
color: 'red'
},
successWrapper: {
'& h4' : {
width: '100%',
color: 'orange'
},
'& $productTitle': {
borderBottom: '1px solid green',
color: 'blue'
}
},
})
const SuccessComp = () => {
const classes = useStyles()
return (<NotifyComp title='Product Added to cart successfully' type="success">
<div >
<h4 >You have add item:</h4>
<h5 className={classes.productTitle}>Product title outside wrapper</h5>
<div className={classes.successWrapper}>
<div className="product">
<h5 className={classes.productTitle}>My first product</h5>
<p>Quantity: 2</p>
</div>
<div className="product">
<h5 className={classes.productTitle}>My second product</h5>
<p>Quantity: 1</p>
</div>
</div>
</div>
</NotifyComp>)
}
export default SuccessComp
SuccessComp.propTypes = {
}
Output:
Chúng ta thấy, mặc dù class productTitle đã được định nghĩa màu đỏ, nhưng được ghi đè màu xanh da trời thông qua bộ chọn JSS.
JSS cung cấp khả năng tạo các quy tắc tương tự với bộ chọn trong CSS tiêu chuẩn, nhưng điểm mạnh là tạo ra các tên class duy nhất không xung đột nhau.
Một lợi thế cuối cùng của JSS là giúp chúng ta 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ì chúng ta đang sử dụng react-jss, có thể chuyển các props cho đối tượng style để tạo định kiểu động. Để kiểm tra điều này, chúng ta cấu trúc lại component NotifyComp.js để sử props cụ và biến để tạo thuộc tính động.
import React from "react";
import PropTypes from 'prop-types';
import './NotifyComp.css'
import { createUseStyles } from 'react-jss';
const color = {
success: 'green',
error: 'red',
}
const useStyles = createUseStyles({
notifyWrapper: {
border: ({type}) => `1px solid ${color[type]}`,
padding: 15,
marginBottom: 15,
'& h3': {
fontWeight: 'bold',
margin: 0,
color: ({type}) => color[type]
}
}
})
const NotifyComp = ({ children, title, type }) => {
const classes = useStyles({type})
return (<div className={classes.notifyWrapper}>
<h3 >{title}</h3>
{children}
</div>)
}
export default NotifyComp
NotifyComp.propTypes = {
title: PropTypes.string,
type: PropTypes.string.isRequired,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element.isRequired
])
};
NotifyComp.defaultProps = {
title: '',
type: '',
children: null
}
Trong bài viết này này, bạn đã tiếp cận được cách định kiểu cho các component bằng cách sử dụng thư viện npm react-jss, hi vọng, với nội dung này bạn đã có thể dễ dàng style cho các component CSS theo nhiều cách khác nhau để áp dụng cho các tình huống phù hợp trong dự án. Chúc bạn thành công !