【React 系列 01】❤️ 在工作中对于 Custom React Hooks 一些思考

当我刚接手 React 项目的时候,就对整体项目代码看了一遍,其中就有一个命名为 customer-hooks,打开一看,全都是命名为 usexxx 的 jsx 文件,后面了解到这是大佬们封装的自定义 hook。

于是,今天就自己来总结一下对于 Custom React Hooks 一些思考。

【React 系列 01】❤️ 在工作中对于 Custom React Hooks 一些思考_第1张图片

自定义 Hook

以下来自 React 官网 关于自定义 Hook 的介绍:

与 React 组件不同的是,自定义 Hook 不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回什么(如果需要的话)。换句话说,它就像一个正常的函数。但是它的名字应该始终以 use 开头,这样可以一眼看出其符合 Hook 的规则。

而对于 Hook 的规则:

  • 不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。
  • 只在 React 函数中调用 Hook

自定义 Hook 封装了一些逻辑代码,并能够把数据向下传递到渲染树的操作。

启动一个 react 项目

可以通过如下命令快速创建简单 react 应用,详细步骤可以参考官网。

 npx create-react-app react-demo

简单的例子

下面编写我们自定义的 Hook,本人还是比较菜鸡,所以就拿阿里的 ahooks 官方文档 学习了,在 src 文件目录下创建如下目录结构,用来存放我们的自定义 hook。

【React 系列 01】❤️ 在工作中对于 Custom React Hooks 一些思考_第2张图片
代码是来自于 ahooks github 仓库地址

PS:这里就不粘贴源码了,大家可以在上述地址拿到。

我们的父组件代码如下:

import SubApp from './subApp';
import useBoolean from './custom-hooks/useBoolean/index';

function App() {
     
  const [state, {
      toggle, setTrue, setFalse }] = useBoolean(true);
  return (
    <div>
      <p>Effects:{
     JSON.stringify(state)}</p>
      <p>
        <button type="button" onClick={
     () => toggle()}>
          Toggle
        </button>
        <button type="button" onClick={
     setFalse} style={
     {
      margin: '0 16px' }}>
          Set false
        </button>
        <button type="button" onClick={
     setTrue}>
          Set true
        </button>
      </p>
      <SubApp />
    </div>
  )
}

export default App;

通过 npm start 启动项目,可以看到如下界面:

【React 系列 01】❤️ 在工作中对于 Custom React Hooks 一些思考_第3张图片

可以看到,使用了自定义 Hook,我们会省去很多重复性的逻辑代码,在父子组件我们都可以使用自定义的 hook。

思考一

但是,我就有问题了,虽然自定义 hook 很香,但发现没有,当我们重新渲染的时候,这一整块逻辑代码也会跟着运行,如果它的运行可以忽略不计倒还好,但还是要考虑一下万一情况,比如你运行了很多个 hook 呢?

想了想,既然这个 useBoolean 是依赖于返回的 state,那么是不是可以通过 useMemo 来解决问题。

查看 ahooks 源码,果然,就有通过 useMemo 来处理的:

const actions = useMemo(() => {
     
  const reverseValueOrigin = (reverseValue === undefined ? !defaultValue : reverseValue) as D | R;

  // 切换返回值
  const toggle = (value?: D | R) => {
     
    // 强制返回状态值,适用于点击操作
    if (value !== undefined) {
     
      setState(value);
      return;
    }
    setState((s) => (s === defaultValue ? reverseValueOrigin : defaultValue));
  };
  // 设置默认值
  const setLeft = () => setState(defaultValue);
  // 设置取反值
  const setRight = () => setState(reverseValueOrigin);
  return {
     
    toggle,
    setLeft,
    setRight,
  };
}, [defaultValue, reverseValue]);

思考二

上述我是有父子组件,它们都调用了自定义 Hook ,那么我想的是它们会共享状态嘛,毕竟都是返回 state,那么假设我改了父组件的 state,那么子组件也会跟着变吗?

带着好奇心,我操作了一下界面,将父组件调用一下 toggle 方法,结果如下:

【React 系列 01】❤️ 在工作中对于 Custom React Hooks 一些思考_第4张图片
在此基础上,我再调用一下父组件的 setTrue 方法试试,结果如下:

【React 系列 01】❤️ 在工作中对于 Custom React Hooks 一些思考_第5张图片
发现,并不会因为多个组件调用同一个自定义 Hook 而共享状态。

来自 React 官网 的表述:

自定义 Hook 是一种重用状态逻辑的机制(例如设置为订阅并存储当前值),所以每次使用自定义 Hook 时,其中的所有 state 和副作用都是 完全隔离 的。

那么自定义 Hook 如何获取独立的 state?

每次调用 Hook,它都会获取独立的 state。由于我们直接调用了 useBoolean,从 React 的角度来看,我们的组件只是调用了 useState 和 useMemo。

我们可以在一个组件中多次调用 useState 和 useEffect等,它们是完全独立的。

那么,结合官方的说法,理解起来就容易多了,其实我们可以这样想,每次调用会返回一个新的实例,如下述我们使用 useBoolean 代码:

const [state, {
      toggle, setTrue, setFalse }] = useBoolean(true);

其实可以想象成返回新的实例,每个实例都会开一个新的空间存放,那么每个组件引用的那个实例都是完全独立开的,所以就不存在状态共享了,放心使用。

思考三

既然状态隔离了,我在想有啥办法能够共享状态呢?

我想有时候也得需要在组件之间共享状态的。

于是,我又翻了翻公司代码以及问了我导师,我发现居然使用了 context,那么,就得研究一下 context 的一些用法。

结尾

为了让文章少点废话,让大家在简短的时间内能够吸收知识,本篇内容就先到此了,下一篇我们将讲解 React Context 的一些使用方法,来解决上文的思考。

关于本文内容,也欢迎和大家交流讨论。

我是【一百个Chocolate】,如果喜欢的话,可以给本文点个赞,我们下期再见啦~

你可能感兴趣的