久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

利用 React 高階組件實現(xiàn)一個面包屑導(dǎo)航

 悅光陰 2021-10-06

什么是 React 高階組件

React 高階組件就是以高階函數(shù)的方式包裹需要修飾的 React 組件,,并返回處理完成后的 React 組件。React 高階組件在 React 生態(tài)中使用的非常頻繁,,比如react-router 中的 withRouter 以及 react-reduxconnect 等許多 API 都是以這樣的方式來實現(xiàn)的,。

使用 React 高階組件的好處

在工作中,我們經(jīng)常會有很多功能相似,,組件代碼重復(fù)的頁面需求,,通常我們可以通過完全復(fù)制一遍代碼的方式實現(xiàn)功能,但是這樣頁面的維護(hù)可維護(hù)性就會變得極差,,需要對每一個頁面里的相同組件去做更改,。因此,我們可以將其中共同的部分,,比如接受相同的查詢操作結(jié)果,、組件外同一的標(biāo)簽包裹等抽離出來,做一個單獨的函數(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)
);

4. Q&A

  1. 如果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 來包裹 connectreturn value,,即

withRouter(
  connect(mapStateToProps)(({ location, routes }) =>
    BreadcrumbsHoc(location, routes)(BreadcrumbsComponent)
  )
);

其實我們從這里也可以看出,,高階組件同高階函數(shù)一樣,不會對組件的類型造成任何更改,,因此高階組件就如同鏈?zhǔn)秸{(diào)用一樣,,可以任意多層包裹來給組件傳入不同的屬性,在正常情況下也可以隨意調(diào)換位置,,在使用上非常的靈活,。這種可插拔特性使得高階組件非常受React生態(tài)的青睞,很多開源庫里都能看到這種特性的影子,,有空也可以都拿出來分析一下,。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多