完整程式碼:Flexible Layout | CodePenopen_in_new
同一個網站裡,每一頁總會有一部份的元素是共用的,例如Navigation、Header、Footer、Sidebar等,通常我們會將這些重複的UI抽象化成樣板元件,雖然這樣做已經大幅提升元件的復用性,卻未必能保留充足的彈性。真實的產品面對不同的需求,可能會需要實作各種Layout的頁面:
甚至要支援透過互動來改變Layout:
為了因應不同需求,我們可以將需要復用的元件(、)集中在Layout元件()裡:
const Navigation = () => ( <ul> <li><Link to="/page/1">Page One</Link></li> <li><Link to="/page/2">Page Two</Link></li> <li><Link to="/page/3">Page Three</Link></li> </ul>)
const Footer = () => ( <footer> <hr /> This page has footer. </footer>)
const AppLayout = ({ navigation, footer, children }) => ( <> <Navigation /> {children} {footer && <Footer />} </>)
AppLayout.propTypes = { navigation: PropTypes.bool, footer: PropTypes.bool,}
AppLayout.defaultProps = { navigation: true, footer: false,}
在需要套用樣板的頁面就可以直接將當作root component來使用,如果想為特定頁面調整Layout,只需要改變傳入的props即可:
const PageOne = () => ( <AppLayout footer> This is a page wrapped with layout component. </AppLayout>)
const App = () => ( <BrowserRouter> <Switch> <Route exact path="/page/1" component={PageOne} /> <Route component={PageOne} /> </Switch> </BrowserRouter>)
如果每一個頁面的Layout不會隨著使用者的互動而改變,也就是各頁面裡的prop始終不變的話,我們其實可以將其提升為HOC :
const withAppLayout = layoutProps => WrappedComponent => pageProps => ( <AppLayout {...layoutProps}> <WrappedComponent {...pageProps} /> </AppLayout>)
使用方式可以在page level:
const PageTwo = withAppLayout({ footer: true })(() => ( <> This is a page wrapped with page-level layout HOC. </>))
const App = () => ( <BrowserRouter> <Switch> <Route exact path="/page/1" component={PageOne} /> <Route exact path="/page/2" component={PageTwo} /> <Route component={PageOne} /> </Switch> </BrowserRouter>)
甚至可以在route level:
const PageThree = () => ( <> This is a page wrapped with route-level layout HOC. </>)
const App = () => ( <BrowserRouter> <Switch> <Route exact path="/page/1" component={PageOne} /> <Route exact path="/page/2" component={PageTwo} /> <Route exact path="/page/3" component={withAppLayout({ footer: true })(PageThree)} /> <Route component={PageOne} /> </Switch> </BrowserRouter>)
Layout的實作方式可以透過Component的抽象,也可以透過簡單的封裝提供HOC的抽象,的不僅與的保持一致,提高復用性,甚至可以在路由的層級就決定好每一個頁面的Layout,方便集中管理,偶爾遇到需要動態Layout的頁面也能隨時改用元件,傳入不同的props就能根據使用者的行為做出千變萬化的Layout了。