Series SolidJS - Tập 5: Control Flow trong SolidJS là gì?
I. Giới thiệu
Hello everyone, chúng ta lại gặp nhau nữa rồi ✌️
Tập trước trong series mình đã chia sẻ về Lifecycle trong SolidJS và các phương thức, cách chúng hoạt động.
Tập này mình và các bạn sẽ cùng nhau tìm hiểu về Control Flow trong SolidJS nhé.
II. Nội dung
🕹️ Control Flow là gì và các thành phần chính:
- Show
- For
- Index
- Switch
- Dynamic
- Portal
- Error Boundary
1. Control Flow
trong SolidJS là gì?
Việc SolidJS không sử dụng Virtual DOM (chỉ update lên native DOM) cộng với việc sử dụng tùy ý Array.prototype.map để render một danh sách dữ liệu sẽ tạo ra một sự lãng phí lên DOM sau mỗi lần cập nhật.
Từ đấy tác giả và các thành viên trong đội ngũ xây dựng SolidJS đã đưa ra 1 giải pháp được gói gọn trong Control Flow
👉 Giải pháp SolidJS được đưa ra là bọc chúng lại trong các Component
của Control Flow
.
Các Component ấy gồm những gì và chúng sẽ giúp chúng ta giải quyết các công việc gì, hãy cùng mình đọc tiếp xuống dưới nhé ^^
2. Show
trong Control Flow?
Luồng hoạt động cơ bản nhất chúng ta thường hay gặp là các câu so sánh có điều kiện - conditional statement (if - else - else if) hay toán tử so sánh - conditional operator (a ? b : c), (a && b)... 🤯
SolidJS cung cấp Component <Show /> cho phép chúng ta sử dụng để thay thế các flow so sánh có điều kiện và giúp code trở nên dễ đọc và dễ sử dụng hơn.
import { render } from "solid-js/web";
import { createSignal, Show } from "solid-js";
function App() {
const [loggedIn, setLoggedIn] = createSignal(false);
const toggle = () => setLoggedIn(!loggedIn());
return (
<>
<Show
when={loggedIn()}
fallback={<button onClick={toggle}>Log in</button>}
>
<button onClick={toggle}>Log out</button>
</Show>
</>
);
}
render(() => <App />, document.getElementById("app"));
Ví dụ trên fallback
chỉ hoạt động và hiển thị trên giao diện, khi điều kiện so sánh là 1 boolean signal loggedIn() === false
được truyền vào When
Và dĩ nhiên khi loggedIn() === true
giao diện sẽ hiển thị <button onClick={toggle}>Log out</button>
3. For
trong Control Flow?
Component <For/>
trong SolidJS dùng để xử lý việc loop qua một mảng các đối tượng và hiển thị lên giao diện. Khi mảng có sự thay đổi, <For/>
cập nhật hoặc di chuyển các item trong DOM thay vì tạo lại chúng. Hãy xem một ví dụ bên dưới.
import { render } from 'solid-js/web';
import { createSignal, For } from 'solid-js';
function App() {
const [cats, _setCats] = createSignal([
{ id: 'id01', name: 'Pate Kem' },
{ id: 'id02', name: 'Naruto' },
{ id: 'id03', name: 'Luffy' }
]);
return (
<For each={cats()}>{(cat, index) =>
<li>
<span>
{index() + 1}: {cat.name} - {cat.id}
</span>
</li>
}</For>
);
}
render(() => <App />, document.getElementById('app'))
Component <For/>
có một property là each
để nhận dữ liệu (một mảng các phần tử).
Sau đấy chúng ta sẽ pass trực tiếp một callback function (cũng khá tương tự như map callback trong Javascript nhỉ)
Callback trên có đối số đầu tiên chính là từng phần tử (item) trong mảng cats
(đặt tên là cat
) và đối số thứ hai là i
(index - chỉ mục) thể hiện thứ tự của từng phần tử trong mảng.
Lưu ý rằng đối số
index
chỉ là một signal, không phải là một hằng số. Mỗi node được hiển thị sẽ được ghép cặp với từng phần tử trong mảng.
Nếu thay đổi vị trí phần tử trong mảng, thay vì bị hủy và tạo lại thì các node sẽ thay đổi và đánh chỉ mục (index) tương ứng.
4. Index
trong Control Flow?
Component <Index/>
cũng giúp cho chúng ta render một list data, nhưng khác với <For/>
ở chỗ khi render bằng <Index/>
sẽ hạn chế số lần re-render hơn so với <For/>
(tùy thuộc hoàn cảnh...)
Về mặt syntax (cú pháp) thì gần như tương tự với For
<Index each={cats()}>{(cat, index) =>
<li>
<span>
{index() + 1}: {cat.name} - {cat.id}
</span>
</li>
}</Index>
Trong Javascript khi làm việc với giá trị nguyên thủy (primitivies value như string hay number) chúng sẽ luôn được so sánh theo giá trị.
Khi sử dụng <For/>
với các giá trị nguyên thủy hoặc mảng lồng mảng (array of arrays) chúng sẽ gây việc re-render
không cần thiết bởi vì <For/>
quan tâm tới vị trí thay đổi của từng đối tượng, còn <Index/>
thì không.
Vd: khi ta ánh xạ 1 list string value xuống các thẻ <input/>, khi update giá trị của input trong list đấy sẽ khiến cho <input/> đấy bị tạo lại (re-render) 😫
👉 Do đó <Index/>
được cung cấp để giải quyết cho trường hợp trên vì <Index/>
chỉ quan tâm đến sử thay đổi giá trị trên từng phần tử (item)
Hiểu đơn giản là khi làm việc với một mảng các đối tượng (Array Object) thì hãy sử dụng
<For/>
và ngược lại khi làm việc với các giá trị nguyên thủy (string, number, boolean...) hãy sử dụng<Index/>
nhé.
import { render } from "solid-js/web";
import { createSignal, For, Index } from "solid-js";
function App() {
const [cats, setCats] = createSignal([
"Jim",
"Maru",
"Henry",
]);
setTimeout(
() => setCats(["Maru", "Jim", "Jim", "New Cat"]),
2000
);
return (
<ul>
<For each={cats()}>
{(name, index) => {
console.log(`For: rendered ${name}`);
return (
<li>
<span>
{index()}: {name}
</span>
</li>
);
}}
</For>
</ul>
);
}
render(() => <App />, document.getElementById("app"));
Trước khi update signal cats
Sau khi update signal cats
Sử dụng <Index/>
thay cho <For/>
<For each={cats()}>
{(name, index) => {
console.log(`For: rendered ${name()}`);
return (
<li>
<span>
{index()}: {name()}
</span>
</li>
);
}}
</For>
Trước khi update signal cats
Sau khi update signal cats
5. Switch
trong Control Flow?
Như ở trên mình đã giới thiệu <Show/>
cho các case so sánh điều kiện để hiển thị giao diện theo mong muốn.
Nhưng đôi khi chúng ta muốn xử lý các điều kiện có nhiều hơn 2 kết quả, ở trường hợp này chúng ta nên sử dụng <Switch/> và <Match/>
(liên tưởng ngay switch/case nhỉ các bạn 😂)
<Switch fallback={<p>{x()} is between 5 and 10</p>}>
<Match when={x() > 10}>
<p>{x()} is greater than 10</p>
</Match>
<Match when={5 > x()}>
<p>{x()} is less than 5</p>
</Match>
</Switch>
Nếu không match bất kì điều kiện nào thì sẽ render fallback
6. Dynamic
trong Control Flow?
<Dynamic/>
hữu ích khi bạn cần render từ một tập dữ liệu có sẵn.
import { render, Dynamic } from "solid-js/web";
import { For } from "solid-js";
function App() {
const hTags = {
1: "Heading 1",
3: "Heading 3",
5: "Heading 5",
};
return (
<>
<For each={Object.entries(hTags)}>
{([key, value]) => <Dynamic component={`h${key}`}>{value}</Dynamic>}
</For>
</>
);
}
render(() => <App />, document.getElementById("app"));
7. Portal
trong Control Flow?
Đôi khi việc xây dựng các element bên ngoài giao diện gốc của ứng dụng sẽ có lợi về mặt xử lý style, logic hơn là dùng z-index
. Chẳng hạn như việc tạo giao diện Popup Modal hay Tooltip... thay vì style các element đó bằng z-index
hay position các kiểu thì các bạn có thể dùng thử <Portal/>
nhé.
Antd (ReactJS) cũng sử dụng Portal để xử lý các case liên quan tới UI Modal, Tooltip hay Notification...
import { render, Portal } from "solid-js/web";
import "./styles.css";
function App() {
return (
<div>
<h2>Pate Kem.</h2>
<Portal>
<div class="popup">
<h1>Popup</h1>
<p>Some text you might need for something or other.</p>
</div>
</Portal>
</div>
);
}
render(() => <App />, document.getElementById("app"));
file styles.css
.popup {
background: brown;
padding: 1rem;
min-height: 200px;
min-width: 200px;
}
Cùng cấp với giao diện gốc
<div id="app">...</div>
của ứng dụng.
Portal cho phép tạo một thẻ <div/>
, chứa nội dung mà chúng ta thiết lập và xuất hiện bên trong document.body
8. Error Boundary
trong Control Flow?
import { createSignal, createEffect, onCleanup, ErrorBoundary } from "solid-js";
import { render } from "solid-js/web";
function Counter() {
const [count, setCount] = createSignal(0);
const id = setInterval(() => {
setCount(count() + 1);
}, 1000);
onCleanup(() => clearInterval(id));
createEffect(() => {
if (count() >= 5) {
throw new Error("Count is too high!");
}
});
return <div>{count()}</div>;
}
function App() {
return (
<ErrorBoundary fallback={<div>Something went wrong!</div>}>
<Counter />
</ErrorBoundary>
);
}
render(() => <App />, document.getElementById("app"));
Trong đoạn code trên, Component <Counter/> sử dụng createEffect
để kiểm tra giá trị của count, và nếu count() >= 5 thì sẽ throw ra lỗi.
Trong <App/>, Component <Counter/> được bọc bởi một ErrorBoundary
để hiển thị fallback khi có lỗi xảy ra.
III. Tổng kết tập 5
Tập này chúng ta đã cùng tìm hiểu về khái niệm và các thành phần của Control FLow trong SolidJS.
Trong tập sau mình sẽ đi vào phần 🚚 Bindings trong SolidJS nhé:
- Events
- Style
- ClassList
- Refs
- Forwarding Refs
- Spreads
- Directives
Cảm ơn các bạn đã theo dõi tập 5 trong series về SolidJS. Nếu các bạn có thắc mắc hoặc góp ý về bài viết hãy comment giúp mình nhé, cảm ơn các bạn 😘
Tham khảo
https://www.solidjs.com/tutorial/flow_show
https://youtu.be/hdUwDmprSmg
All rights reserved