前端精神小夥:React Hooks 響應式佈局

前言

如今稍微大型的站點都會採用H5/PC端 並行,經過nignx獲取瀏覽器的UA信息來切換站點。前端

但這對於一些企業站點或人手不足的小型項目來講,就很難實現。react

經過CSS媒體查詢實現響應式佈局,是主流方式。git

可是,有時在React程序中,須要根據屏幕大小有條件地渲染不一樣的組件(寫媒體查詢太麻煩了,還不如另寫組件),其實使用React Hooks,能夠更靈活實現。github

本文的實現來自:瀏覽器

Developing responsive layouts with React Hooksbash

1. 方案一:innerWidth

一個很簡單粗略的方案,是個前端都知道:微信

const MyComponent = () => {
  // 當前窗口寬度
  const width = window.innerWidth;
  // 鄰介值
  const breakpoint = 620;
  // 寬度小於620時渲染手機組件,反之桌面組件
  return width < breakpoint ? <MobileComponent /> : <DesktopComponent />;
}
複製代碼

這個簡單的解決方案確定會起做用。根據用戶設備的窗口寬度,咱們能夠呈現桌面視圖或手機視圖。ide

可是,當調整窗口大小時,未解決寬度值的更新問題,可能會渲染錯誤的組件。函數

2. 方案二:Hooks+resize

說着也簡單,監聽resize事件時,觸發useEffect改變數據。佈局

const MyComponent = () => {
  const [width, setWidth] = React.useState(window.innerWidth);
  const breakpoint = 620;

  React.useEffect(() => {
    window.addEventListener("resize", () => setWidth(window.innerWidth));
  }, []);

  return width < breakpoint ? <MobileComponent /> : <DesktopComponent />;
}
複製代碼

但精通Hooks的你,必定知道這裏存在內存性能消耗問題:resize事件沒移除!

優化版本:

const useViewport = () => {
  const [width, setWidth] = React.useState(window.innerWidth);

  React.useEffect(() => {
    const handleWindowResize = () => setWidth(window.innerWidth);
    window.addEventListener("resize", handleWindowResize);
    return () => window.removeEventListener("resize", handleWindowResize);
  }, []);

  return { width };
}
複製代碼

3. 方案三:構建useViewport

自定義React Hooks,能夠將組件/函數最大程度的複用。構建一個也很簡單:

const useViewport = () => {
  const [width, setWidth] = React.useState(window.innerWidth);

  React.useEffect(() => {
    const handleWindowResize = () => setWidth(window.innerWidth);
    window.addEventListener("resize", handleWindowResize);
    return () => window.removeEventListener("resize", handleWindowResize);
  }, []);

  return { width };
}
複製代碼

精簡後的組件代碼:

const MyComponent = () => {
  const { width } = useViewport();
  const breakpoint = 620;

  return width < breakpoint ? <MobileComponent /> : <DesktopComponent />;
}
複製代碼

可是這裏還有另外一個性能問題:

響應式佈局影響的是多個組件,若是在多處使用useViewport,這將浪費性能。

這時就須要另外一個React親兒子:React Context(上下文) 來幫忙。

4.終極方案:Hooks+Context

咱們將建立一個新的文件viewportContext,在其中能夠存儲當前視口大小的狀態以及計算邏輯。

const viewportContext = React.createContext({});

const ViewportProvider = ({ children }) => {
  // 順帶監聽下高度,備用
  const [width, setWidth] = React.useState(window.innerWidth);
  const [height, setHeight] = React.useState(window.innerHeight);

  const handleWindowResize = () => {
    setWidth(window.innerWidth);
    setHeight(window.innerHeight);
  }

  React.useEffect(() => {
    window.addEventListener("resize", handleWindowResize);
    return () => window.removeEventListener("resize", handleWindowResize);
  }, []);

  return (
    <viewportContext.Provider value={{ width, height }}>
      {children}
    </viewportContext.Provider>
  );
};

const useViewport = () => {
  const { width, height } = React.useContext(viewportContext);
  return { width, height };
}
複製代碼

緊接着,你須要在React根節點,確保已經包裹住了App

const App = () => {
  return (
    <ViewportProvider>
      <AppComponent />
    </ViewportProvider>
  );
}
複製代碼

在日後的每次useViewport(),其實都只是共享Hooks

const MyComponent = () => {
  const { width } = useViewport();
  const breakpoint = 620;

  return width < breakpoint ? <MobileComponent /> : <DesktopComponent />;
}
複製代碼

後記

github上面的響應式佈局hooks,都是大同小異的實現方式。

本文除了介紹React Hooks的響應式佈局實現,還介紹瞭如何自定義hooks與使用Context上下文,來複用,以達到性能最佳優化。

❤️ 看完三件事

若是你以爲這篇內容對你挺有啓發,我想邀請你幫我三個小忙:

  1. 點贊,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓 -_-)
  2. 關注公衆號「前端勸退師」,不按期分享原創知識。
  3. 也看看其它文章

勸退師我的微信:huab119

也能夠來個人GitHub博客裏拿全部文章的源文件:

前端勸退指南github.com/roger-hiro/… 一塊兒玩耍呀。~

相關文章
相關標籤/搜索