您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“如何利用React高階組件實現(xiàn)一個面包屑導(dǎo)航”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
什么是 react 高階組件
react 高階組件就是以高階函數(shù)的方式包裹需要修飾的 react 組件,并返回處理完成后的 react 組件。react 高階組件在 react 生態(tài)中使用的非常頻繁,比如react-router 中的 withrouter 以及 react-redux 中 connect 等許多 api 都是以這樣的方式來實現(xiàn)的。
使用 react 高階組件的好處
在工作中,我們經(jīng)常會有很多功能相似,組件代碼重復(fù)的頁面需求,通常我們可以通過完全復(fù)制一遍代碼的方式實現(xiàn)功能,但是這樣頁面的維護(hù)可維護(hù)性就會變得極差,需要對每一個頁面里的相同組件去做更改。因此,我們可以將其中共同的部分,比如接受相同的查詢操作結(jié)果、組件外同一的標(biāo)簽包裹等抽離出來,做一個單獨(dú)的函數(shù),并傳入不同的業(yè)務(wù)組件作為子組件參數(shù),而這個函數(shù)不會修改子組件,只是通過組合的方式將子組件包裝在容器組件中,是一個無副作用的純函數(shù),從而我們能夠在不改變這些組件邏輯的情況下將這部分代碼解耦,提升代碼可維護(hù)性。
自己動手實現(xiàn)一個高階組件
前端項目里,帶鏈接指向的面包屑導(dǎo)航十分常用,但由于面包屑導(dǎo)航需要手動維護(hù)一個所有目錄路徑與目錄名映射的數(shù)組,而這里所有的數(shù)據(jù)我們都能從 react-router 的路由表中取得,因此我們可以從這里入手,實現(xiàn)一個面包屑導(dǎo)航的高階組件。
首先我們看看我們的路由表提供的數(shù)據(jù)以及目標(biāo)面包屑組件所需要的數(shù)據(jù):
// 這里展示的是 react-router4 的route示例 let routes = [ { breadcrumb: '一級目錄', path: '/a', component: require('../a/index.js').default, items: [ { breadcrumb: '二級目錄', path: '/a/b', component: require('../a/b/index.js').default, items: [ { breadcrumb: '三級目錄1', path: '/a/b/c1', component: require('../a/b/c1/index.js').default, exact: true, }, { breadcrumb: '三級目錄2', path: '/a/b/c2', component: require('../a/b/c2/index.js').default, exact: true, }, } ] } ] // 理想中的面包屑組件 // 展示格式為 a / b / c1 并都附上鏈接 const breadcrumbscomponent = ({ breadcrumbs }) => ( <div> {breadcrumbs.map((breadcrumb, index) => ( <span key={breadcrumb.props.path}> <link to={breadcrumb.props.path}>{breadcrumb}</link> {index < breadcrumbs.length - 1 && <i> / </i>} </span> ))} </div> );
這里我們可以看到,面包屑組件需要提供的數(shù)據(jù)一共有三種,一種是當(dāng)前頁面的路徑,一種是面包屑所帶的文字,一種是該面包屑的導(dǎo)航鏈接指向。
其中第一種我們可以通過 react-router 提供的 withrouter 高階組件包裹,可使子組件獲取到當(dāng)前頁面的 location 屬性,從而獲取頁面路徑。
后兩種需要我們對 routes 進(jìn)行操作,首先將 routes 提供的數(shù)據(jù)扁平化成面包屑導(dǎo)航需要的格式,我們可以使用一個函數(shù)來實現(xiàn)它。
/** * 以遞歸的方式展平react router數(shù)組 */ const flattenroutes = arr => arr.reduce(function(prev, item) { prev.push(item); return prev.concat( array.isarray(item.items) ? flattenroutes(item.items) : item ); }, []);
之后將展平的目錄路徑映射與當(dāng)前頁面路徑一同放入處理函數(shù),生成面包屑導(dǎo)航結(jié)構(gòu)。
export const getbreadcrumbs = ({ flattenroutes, location }) => { // 初始化匹配數(shù)組match let matches = []; location.pathname // 取得路徑名,然后將路徑分割成每一路由部分. .split('?')[0] .split('/') // 對每一部分執(zhí)行一次調(diào)用`getbreadcrumb()`的reduce. .reduce((prev, cursection) => { // 將最后一個路由部分與當(dāng)前部分合并,比如當(dāng)路徑為 `/x/xx/xxx` 時,pathsection分別檢查 `/x` `/x/xx` `/x/xx/xxx` 的匹配,并分別生成面包屑 const pathsection = `${prev}/${cursection}`; const breadcrumb = getbreadcrumb({ flattenroutes, cursection, pathsection, }); // 將面包屑導(dǎo)入到matches數(shù)組中 matches.push(breadcrumb); // 傳遞給下一次reduce的路徑部分 return pathsection; }); return matches; };
然后對于每一個面包屑路徑部分,生成目錄名稱并附上指向?qū)?yīng)路由位置的鏈接屬性。
const getbreadcrumb = ({ flattenroutes, cursection, pathsection }) => { const matchroute = flattenroutes.find(ele => { const { breadcrumb, path } = ele; if (!breadcrumb || !path) { throw new error( 'router中的每一個route必須包含 `path` 以及 `breadcrumb` 屬性' ); } // 查找是否有匹配 // exact 為 react router4 的屬性,用于精確匹配路由 return matchpath(pathsection, { path, exact: true }); }); // 返回breadcrumb的值,沒有就返回原匹配子路徑名 if (matchroute) { return render({ content: matchroute.breadcrumb || cursection, path: matchroute.path, }); } // 對于routes表中不存在的路徑 // 根目錄默認(rèn)名稱為首頁. return render({ content: pathsection === '/' ? '首頁' : cursection, path: pathsection, }); };
之后由 render 函數(shù)生成最后的單個面包屑導(dǎo)航樣式。單個面包屑組件需要為 render 函數(shù)提供該面包屑指向的路徑 path, 以及該面包屑內(nèi)容映射content 這兩個 props。
/** * */ const render = ({ content, path }) => { const componentprops = { path }; if (typeof content === 'function') { return <content {...componentprops} />; } return <span {...componentprops}>{content}</span>; };
有了這些功能函數(shù),我們就能實現(xiàn)一個能為包裹組件傳入當(dāng)前所在路徑以及路由屬性的 react 高階組件了。傳入一個組件,返回一個新的相同的組件結(jié)構(gòu),這樣便不會對組件外的任何功能與操作造成破壞。
const breadcrumbshoc = ( location = window.location, routes = [] ) => component => { const breadcomponent = ( <component breadcrumbs={getbreadcrumbs({ flattenroutes: flattenroutes(routes), location, })} /> ); return breadcomponent; }; export default breadcrumbshoc;
調(diào)用這個高階組件的方法也非常簡單,只需要傳入當(dāng)前所在路徑以及整個 react router 生成的 routes 屬性即可。
至于如何取得當(dāng)前所在路徑,我們可以利用 react router 提供的 withrouter 函數(shù),如何使用請自行查閱相關(guān)文檔。
值得一提的是,withrouter 本身就是一個高階組件,能為包裹組件提供包括 location 屬性在內(nèi)的若干路由屬性。所以這個 api 也能作為學(xué)習(xí)高階組件一個很好的參考。
withrouter(({ location }) => breadcrumbshoc(location, routes)(breadcrumbscomponent) );
q&a
如果react router 生成的 routes 不是由自己手動維護(hù)的,甚至都沒有存在本地,而是通過請求拉取到的,存儲在 redux 里,通過 react-redux 提供的 connect 高階函數(shù)包裹時,路由發(fā)生變化時并不會導(dǎo)致該面包屑組件更新。使用方法如下:
function mapstatetoprops(state) { return { routes: state.routes, }; } connect(mapstatetoprops)( withrouter(({ location }) => breadcrumbshoc(location, routes)(breadcrumbscomponent) ) );
這其實是 connect 函數(shù)的一個bug。因為 react-redux 的 connect 高階組件會為傳入的參數(shù)組件實現(xiàn) shouldcomponentupdate 這個鉤子函數(shù),導(dǎo)致只有 prop 發(fā)生變化時才觸發(fā)更新相關(guān)的生命周期函數(shù)(含 render),而很顯然,我們的 location 對象并沒有作為 prop 傳入該參數(shù)組件。
官方推薦的做法是使用 withrouter 來包裹 connect 的 return value,即
withrouter( connect(mapstatetoprops)(({ location, routes }) => breadcrumbshoc(location, routes)(breadcrumbscomponent) ) );
“如何利用React高階組件實現(xiàn)一個面包屑導(dǎo)航”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。