精读《怎么用 React Hooks 造轮子》

1 引言

上周的 精读《React Hooks》 已经实现了对 React Hooks 的基本认知,也许你也看了 React Hooks 基本实现剖析(就是数组),但理解实现原理就可以用好了吗?学的是知识,而用的是技能,看别人的用法就像刷抖音一样(哇,饭还可以这样吃?),你总会有新的收获。

这篇文章将这些知识实践起来,看看广大程序劳动人民是如何发掘 React Hooks 的潜力的(造什么轮子)。

首先,站在使用角度,要理解 React Hooks 的特点是 “非常方便的 Connect 一切”,所以无论是数据流、Network,或者是定时器都可以监听,有一点 RXJS 的意味,也就是你可以利用 React Hooks,将 React 组件打造成:任何事物的变化都是输入源,当这些源变化时会重新触发 React 组件的 render,你只需要挑选组件绑定哪些数据源(use 哪些 Hooks),然后只管写 render 函数就行了!

2 精读

参考了部分 React Hooks 组件后,笔者按照功能进行了一些分类。

由于 React Hooks 并不是非常复杂,所以就不按照技术实现方式去分类了,毕竟技术总有一天会熟练,而且按照功能分类才有持久的参考价值。

DOM 副作用修改 / 监听

做一个网页,总有一些看上去和组件关系不大的麻烦事,比如修改页面标题(切换页面记得改成默认标题)、监听页面大小变化(组件销毁记得取消监听)、断网时提示(一层层装饰器要堆成小山了)。而 React Hooks 特别擅长做这些事,造这种轮子,大小皆宜。

由于 React Hooks 降低了高阶组件使用成本,那么一套生命周期才能完成的 “杂耍” 将变得非常简单。

下面举几个例子:

修改页面 title

效果:在组件里调用 useDocumentTitle 函数即可设置页面标题,且切换页面时,页面标题重置为默认标题 “前端精读”。

useDocumentTitle("个人中心");

实现:直接用 document.title 赋值,不能再简单。在销毁时再次给一个默认标题即可,这个简单的函数可以抽象在项目工具函数里,每个页面组件都需要调用。

function useDocumentTitle(title) {
  useEffect(
    () => {
      document.title = title;
      return () => (document.title = "前端精读");
    },
    [title]
  );
}

在线 Demo

监听页面大小变化,网络是否断开

效果:在组件调用 useWindowSize 时,可以拿到页面大小,并且在浏览器缩放时自动触发组件更新。

const windowSize = useWindowSize();
return 
页面高度:{windowSize.innerWidth}
;

实现:和标题思路基本一致,这次从 window.innerHeight 等 API 直接拿到页面宽高即可,注意此时可以用 window.addEventListener('resize') 监听页面大小变化,此时调用 setValue 将会触发调用自身的 UI 组件 rerender,就是这么简单!

最后注意在销毁时,removeEventListener 注销监听。

function getSize() {
  return {
    innerHeight: window.innerHeight,
    innerWidth: window.innerWidth,
    outerHeight: window.outerHeight,
    outerWidth: window.outerWidth
  };
}

function useWindowSize() {
  let [windowSize, setWindowSize] = useState(getSize());

  function handleResize() {
    setWindowSize(getSize());
  }

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

  return windowSize;
}

在线 Demo

动态注入 css

效果:在页面注入一段 class,并且当组件销毁时,移除这个 class。

const className = useCss({
  color: "red"
});

return 
Text.
;

实现:可以看到,Hooks 方便的地方是在组件销毁时移除副作用,所以我们可以安心的利用 Hooks 做一些副作用。注入 css 自然不必说了,而销毁 css 只要找到注入的那段引用进行销毁即可,具体可以看这个 代码片段

DOM 副作用修改 / 监听场景有一些现成的库了,从名字上就能看出来用法: document-visibilitynetwork-statusonline-statuswindow-scroll-positionwindow-sizedocument-title

组件辅助

Hooks 还可以增强组件能力,比如拿到并监听组件运行时宽高等。

获取组件宽高

效果:通过调用 useComponentSize 拿到某个组件 ref 实例的宽高,并且在宽高变化时,rerender 并拿到最新的宽高。

const ref = useRef(null);
let componentSize = useComponentSize(ref);

return (
  <>
    {componentSize.width}