Một sai lầm phổ biến trong React mà bạn thường hay mắc phải

Một điều mà chúng ta thường làm khi bắt đầu nhận một UI từ designer là gì? Là nhìn vào design và phân tích thành các component nhỏ. Việc làm này giúp code chúng ta được phân tách thành từng phần và tiện cho việc quản lý code.

Ví dụ, đây là trang chủ Github của mình:

Nhìn vào design ta có thể xác định được các component khác nhau rồi phải không nào? Đây là cách mình nhìn nó:

Bạn có thể để nguyên 1 page vào code luôn mà không chia ra các component, hoặc phân tách như mình, hoặc nhiều component hơn tùy bạn. Nó phụ thuộc vào nhiều yếu tố mà mình không tiện đi sâu vào. Sai lầm mà mình muốn nhắc đến ở đây là cách chúng ta sắp xếp các UI elements lại với nhau trong code dẫn đến sự khó khăn về bảo trì code sau này (ví dụ như nỗi đau prop-drilling).

Cùng tưởng tượng thử cách chúng ta cấu trúc các component cho trang này:

jsx
function App() {
return (
<div>
<MainNav />
<Homepage />
</div>
)
}
function MainNav() {
return (
<div>
<GitHubLogo />
<SiteSearch />
<NavLinks />
<NotificationBell />
<CreateDropdown />
<ProfileDropdown />
</div>
)
}
function Homepage() {
return (
<div>
<LeftNav />
<CenterContent />
<RightContent />
</div>
)
}
function LeftNav() {
return (
<div>
<DashboardDropdown />
<Repositories />
<Teams />
</div>
)
}
function CenterContent() {
return (
<div>
<RecentActivity />
<AllActivity />
</div>
)
}
function RightContent() {
return (
<div>
<Notices />
<ExploreRepos />
</div>
)
}

Mình nghĩ như vậy khá ổn rồi và đây cũng là cách mà chúng ta thường code. Trước khi mình nói về các vấn đề tiềm ẩn của cách tiếp cận này, mình sẽ cho bạn thấy một cấu trúc khác. Sau đó mình sẽ nói về sự cải thiện của nó.

jsx
function App() {
return (
<div>
<MainNav>
<GitHubLogo />
<SiteSearch />
<NavLinks />
<NotificationBell />
<CreateDropdown />
<ProfileDropdown />
</MainNav>
<Homepage
leftNav={
<LeftNav>
<DashboardDropdown />
<Repositories />
<Teams />
</LeftNav>
}
centerContent={
<CenterContent>
<RecentActivity />
<AllActivity />
</CenterContent>
}
rightContent={
<RightContent>
<Notices />
<ExploreRepos />
</RightContent>
}
/>
</div>
)
}
function MainNav({ children }) {
return <div>{children}</div>
}
function Homepage({ leftNav, centerContent, rightContent }) {
return (
<div>
{leftNav}
{centerContent}
{rightContent}
</div>
)
}
function LeftNav({ children }) {
return <div>{children}</div>
}
function CenterContent({ children }) {
return <div>{children}</div>
}
function RightContent({ children }) {
return <div>{children}</div>
}

Phản ứng đầu tiên của bạn với điều này là gì? Sốc 😮 ? Kinh ngạc 😀 ? Tò mò 🙄 ? Hoang mang 😳 ? Thực ra thì nó cũng phụ thuộc vào việc bạn có quen với cách thứ 2 này không.

Nhưng mà… Tại sao?

Tại sao chúng ta phải làm điều này? Nguyên nhân lớn nhất là quản lý state. Hãy tưởng tượng bạn có state user tại App component, bạn muốn truyền xuống cho component con để dùng. Việc đầu tiên chúng ta nghỉ đến là prop-drilling.

Ví dụ: <App /> -> <Homepage /> -> <CenterContent /> -> <AllActivity /> (và có thể sâu hơn nữa). Thường thì đây là một nổi khổ tâm của mọi dev React, có thể bạn sẽ chuyển sang sử dụng Context API để giải quyết vấn đề này.

Nhưng với cách thứ hai của chúng ta thì nó sẽ trông như thế nào? <App /> -> <AllActivity />. Boom. Chỉ vậy thôi.

Tóm lại

Mình nghĩ nhiều người sẽ đổi từ việc truyền prop sang context (hoặc Redux) một cách nhanh chóng khi gặp case này. Nếu chúng ta cấu trúc các component một cách khoa học, sắp xếp chúng lại một tí thôi thì chúng ta cũng không cần đền "đao to búa lớn" như Context API hay Redux làm gì, ngoài ra nó cũng giúp cho việc bảo trì trở nên dễ dàng hơn và bạn cũng phải mở ít file hơn nếu có thay đổi gì đó trong props

Tham khảo

One React mistake that’s slowing you down