一:Hook概念篇
1.hook简介
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。Hook 为已知的 React 概念提供了更直接的 API:props, state,context,refs 以及生命周期。稍后我们将看到,Hook 还提供了一种更强大的方式来组合他们。
2.为什么使用hook
-
(1) 在组件之间复用状态逻辑很难 React 没有提供将可复用性行为“附加”到组件的途径(例如,把组件连接到 store)。如果你使用过 React 一段时间,你也许会熟悉一些解决此类问题的方案,比如 render props 和 高阶组件。但是这类方案需要重新组织你的组件结构,这可能会很麻烦,使你的代码难以理解。如果你在 React DevTools 中观察过 React 应用,你会发现由 providers,consumers,高阶组件,render props 等其他抽象层组成的组件会形成“嵌套地狱”。尽管我们可以在 DevTools 过滤掉它们,但这说明了一个更深层次的问题:React 需要为共享状态逻辑提供更好的原生途径。(例:antd3x表单和antd4x表单) 你可以使用 Hook 从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使你在无需修改组件结构的情况下复用状态逻辑。 这使得在组件间或社区内共享 Hook 变得更便捷。
-
(2) 复杂组件难以理解 我们经常维护一些组件,组件起初很简单,但是逐渐会被状态逻辑和副作用充斥。每个生命周期常常包含一些不相关的逻辑。例如,组件常常在 componentDidMount 和 componentDidUpdate 中获取数据。但是,同一个 componentDidMount 中可能也包含很多其它的逻辑,如设置事件监听,而之后需在 componentWillUnmount 中清除。相互关联且需要对照修改的代码被进行了拆分,而完全不相关的代码却在同一个方法中组合在一起。如此很容易产生 bug,并且导致逻辑不一致。 为了解决这个问题,Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分。你还可以使用 reducer 来管理组件的内部状态,使其更加可预测。
-
(3) class组件对于部分初学者难以理解 除了代码复用和代码管理会遇到困难外,我们还发现 class 是学习 React 的一大屏障。你必须去理解 JavaScript 中 this 的工作方式,这与其他语言存在巨大差异。还不能忘记绑定事件处理器。没有稳定的语法提案,这些代码非常冗余。大家可以很好地理解 props,state 和自顶向下的数据流,但对 class 却一筹莫展。即便在有经验的 React 开发者之间,对于函数组件与 class 组件的差异也存在分歧,甚至还要区分两种组件的使用场景。 为了解决这些问题,Hook 使你在非 class 的情况下可以使用更多的 React 特性。 从概念上讲,React 组件一直更像是函数。而 Hook 则拥抱了函数,同时也没有牺牲 React 的精神原则。Hook 提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术。(例:redux/redux-toolkit,alibaba/hooks , react-use …)
-
(4) hook的优点
- 简洁:React Hooks解决了HOC和Render Props的嵌套问题,更加简洁
- 解耦:React Hooks可以更方便地把 UI 和状态分离,做到更彻底的解耦,无影响复用组件逻辑:Hook 使你在无需修改组件结构的情况下复用状态逻辑
- 函数友好:React Hooks为函数组件而生,从而解决了类组件的几大问题this 指向容易错误,分割在不同声明周期中的逻辑使得代码难以理解和维护代码复用成本高(高阶组件容易使代码量剧增)
3.hook使用规则
- (1) 只在最顶层使用hook 不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。 例:比如useState,当我们使用多个useState来存储数据时,函数组件在渲染时会将useState按照调用顺序以链表的形式挂载到当前的fiber node 中memoizedState属性上。如果我们在条件语句和循环调用hook时,数据就会发生错位。
- (2)只在React函数中调用 不要在普通的 JavaScript 函数中调用 Hook。你可以: ✅ 在 React 的函数组件中调用 Hook ✅ 在自定义 Hook 中调用其他 Hook
二:hook实战篇
hook使用和自定义hook
因为hook的api使用比较简单,基本看官方文档就能使用,我这里简单例了useState的一个使用。
所以我们这里主要讲自定义hook的使用,直接上代码,实现一个时间器。 大家可以看一下下面的一个项目结构,我在hook的一个文件夹新建了一个useSystem.js的一个文件,里面定义了一个useClock的一个自定义hook
我们通过export导出当前的一个hook到我的components文件夹中的Hook.jsx文件使用
再看一下我们的控制台
每当useClock触发一次,时间都会更新,这里我截了一个静态的图,我们这里再写一个api请求用户数据的一个hook,看下图,我新建了一个useUser.js文件专门用来对用户的一些操作,自定义了一个useGetUserInfo的一个hook,使用setTimeout在Effect中去模拟请求api接口的一个操作,然后return了一个数组回去,数组里面存放了一个user的数据和editUser的一个函数(这里有一个技巧,为什么我会return一个数组回去?待会看我调用就知道了。。。)
我们在Hook.jsx调用这个自定义的hook
你们注意看箭头方向的函数,是不是和我们useGetUserInfo中定义的不一样?(为什么我在自定义hook时return的是一个数组?),因为你return一个数组的话,你在调用时是通过下标去对应调用的,所以我们可以自定义方法的名称(只要你下标的方法对应修改的那个方法就行了),这样有什么好处?好处就是我们可以重新自定义方法的名称,更加的灵活,而且更加接近原生的hook的api,比如useState,useReducer等。。。我们点击button调用修改的方法,看控制台输出
点击后用户名称已经改变了,说明我们的这个自定义hook也是没问题的,我们也可以自定义多个hook来使用,比如
这样就大大减少了我们代码的简洁和复用的一个问题,今天就说到这里,希望对你们有帮助!