当面试官问自定义Hook的时候他想知道什么

希沃ENOW大前端javascript

公司官网:CVTE(广州视源股份)css

团队:CVTE旗下将来教育希沃软件平台中心enow团队html

本文做者:前端

一兵名片.png

前言

在听到这么一个问题的时候,首先脑海中整体思路是这样子的:java

  • 认识自定义Hook的基本概念
  • 可以使用自定义Hook进行复用逻辑的封装
  • 了解自定义Hook的最佳实践与工做原理
  • 进一步深刻Hook底层原理思想
  • 创造新的轮子思想,借鉴Reack Hook思想

什么是React自定义Hook

HookReact 16.8 的新增特性。它可让你在不编写 class 的状况下使用 state 以及其余的 React 特性。React官方提供了经常使用的StateHookEffectHook分别用以管理函数式组件状态和反作用。(React Hook官方介绍)react

除了官方提供的StateHook和EffectHook外,咱们能够本身将经常使用的组件逻辑抽取到可重用的函数中,该函数须以React约定的形式来命名与使用,用以共享组件逻辑。git

那么如何编写咱们本身的自定义Hook

经过一个简单的例子来看一下 好比咱们须要在拿到某个数据后设置页面标题,并且须要在各个页面里面使用github

import React, { useState, useEffect } from 'react';

export default function App() { 
    // 正常咱们这样子实现
    const [title, setTitle] = useState('默认标题')
    
    useEffect(() => {
        document.title = title;
    }, [title]);
    
    const onTitleChange = (event) => {
        setTitle(event.target.value);
    }
    
    return (<div> <h2>{title}</h2> <input type="text" onInput={onTitleChange}/> </div>)
}
复制代码
  • 抽取共用逻辑,封装成自定义Hook
export function useSetPageTitle (initTitle) {
  const [title, setTitle] = useState(initTitle || '默认标题');

  useEffect(() => {
    document.title = title;
  }, [title]);

  return [title, setTitle]
}
复制代码
  • 在其余组件中使用刚刚写的useSetPageTitle
import { useSetPageTitle } from '../App';

export default function Chirld() { 
    // 这里使用刚才写自定义Hook
    const [title, setTitle] = useSetPageTitle();
    
    return (<div> <h2>{title}</h2> <input type="text" onInput={onTitleChange}/> </div>>)
}
复制代码

这样子一个自定义的hook就成型了,是否是瞬间感受逼格提升了,原来我也能够写个这么高大上的自定义Hook,哈哈哈。npm

聪明的大家必定发现了下面的特色数组

React约定自定义 Hook 必须以 use 开头

上面的例子中使用【useSetPageTitle】。不遵循的话,因为没法判断某个函数是否包含对其内部 Hook 的调用,React 将没法自动检查你的 Hook 是否违反了 Hook 的规则。

自定义Hook可自由搭配其余hook使用

你能够自由使用其余内部Hook和其余自定义Hook.上面演示例子中使用useStateuseEffect.

只在 React 的函数组件中最顶层使用 Hook

这样作是为了确保 Hook 在每一次渲染中都按照一样的顺序被调用。若是不是最顶层会致使状态或者执行方法出错进而致使BUG

  • 反例

state='A'条件知足时执行调用Hook顺序是正常,当后续渲染条件不知足时,则React调用Hook顺序出错,则会致使方法和状态逻辑执行出错。

import React, { useState, useEffect } from 'react';
import './App.css';
import { useSetPageTitle } from './hooks';

export default function App() {
  const [state, setState] = useState('A');
  
  // 反例
  if (state === 'A') { 
      useEffect(() => {
        console.log('只执行一次')
      }, [state]);
  }
 
  useState({});

  useEffect(() => {
    // 获取数据
  }, []);

  useState({});

  console.log(title)

  return (
    <div className="App"> <p> Hello React Hook! </p> </div>
  );
}
复制代码
  • 正例
import React, { useState, useEffect } from 'react';

export default function App() {

  const [state, setState] = useState('A');
  
  useEffect(() => {
    if (state === 'A') { 
        console.log('执行相应的逻辑')
    }
   
  }, [state]);

  useState({});

  useEffect(() => {
    // 获取数据
  }, []);

  useState({});



  return (
    <div className="App"> <p> Hello React Hook! </p> </div>
  );
}
复制代码

为了不出错咱们可使用ESLint插件来检测强制执行这些规则。 另外,亲测React官方提供的create-react-app @3.x与@4.x在eject后已经集成了插件

eslint-plugin-react-hooks
复制代码
// 你的 ESLint 配置
{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则
    "react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖
  }
}
复制代码

那么咱们能够在哪些场景识别抽取自定义hook呢?

自定义 Hook 解决了之前在React组件中没法灵活共享逻辑的问题。那么业务开发中可建立各类的自定义Hook,公共辅助函数、状态复用、逻辑复用、效果复用、操做复用、生命周期模拟以及多种组合复用等只有想不到,相信很难有这么聪明的大家作不到的场景实现。

下面举几个简单栗子,抛砖引玉

  • 例如使用useMount模拟生命周期componentDidMount
import React, { useState, useEffect } from 'react';

function useMount (fn) {
  useEffect(() => {
    fn();
  }, []);
}

export default function App() {

  useMount(() => {
     // 你的逻辑
  })


  return (
    <div className="App"> <p> Hello React Hook! </p> </div>
  );
}
复制代码
  • 利用useEffect返回函数是销毁才调用的机制来模拟unmount
function useUnmount (fn) {
    useEffect(() => {
        return () => {
            fn();
        }
    }, []);
}

export default function App() {

   useUnmount(() => {
        console.log('销毁组件时输出')
   })
 

  return (
    <div className="App"> <p> Hello React Hook! </p> </div>
  );
}
复制代码
  • 监听窗口变化
import React, { useState, useEffect } from 'react';

function useOnResize (fn) {
    useEffect(() => {
        window.addEventListener('resize',fn);
        return () => {
            window.removeEventListener('resize',fn)
        }
    }, []);
}

export default function App() {

   useOnResize(() => {
        console.log(document.body.clientWidth)
   })
 
  return (
    <div className="App"> <p> Hello React Hook! </p> </div>
  );
}
复制代码

这里介绍一些有趣的Hooks库供使用与参考

由蚂蚁 umi 团队、淘系 ice 团队以及阿里体育团队共同建设的 React Hooks 工具库ahooks

Set of a helpful hooks, for different specific to some primitives types state changing helpers.

可使建立弹窗,提示,菜单变得很是容易,提供了建立DOM层次以外的元素的功能

用于发起Http请求的优秀Hook

发现更多优秀Hook库可在下面评论贴上,整一个优秀Hooks库的集合

进阶Hooks优秀实践与工做原理

笔者并无实际看过React源码,仅看过一些React Hook工做原理的文章。 这里抛转引玉,看过几个比较好来分享。欢迎留言更多优秀实践和原理剖析。

创造新轮子

迎(bu)接(yao)新(geng)轮(xin)子(le),向(biao)着(shi)新(xue)轮(bu)子(dong)前(le)进

参考资料

hooks-custom官方介绍

相关文章
相关标签/搜索