React Hooks 是什么?

虽然近几年大部分开发 React 都是使用 Hooks,但 Hooks 是在 React 16.8 之后出现的,在面试时也会被问到 Hooks 的相关问题,本篇文章总结了

什么是 Hook?Hook 解决了什么问题?Hook 的规则?常使用的 Hook?什么是 Hook?在 React 16.8 之前,如果我们想要使用到生命周期的方法或状态 (state),那我们只能使用 React 的 class component。但 React hooks 的出现,让我们即使使用 functional component,也能够使用到 React 的功能和状态。在 React 中,用 use 开头的函式我们会把它称之为 Hook,有些 Hooks 是 React 内建的功能,例如:useState、useEffect,但我们也可以创造自己的 Hooks。 Hooks 相较于函式更严谨、有一些需要遵守的规范,我们接下来在下方会提到。

Hook 解决了什么问题?React 团队开发 Hooks 主要为了解决这三个原因

状态相关的逻辑 (stateful logics) 在 class component 之间难以复用

在使用 class component 时,React 并没有提供可以将重复的逻辑添加到元件的方法。因此,开发者可能使用 render props 或是 higher-order components 的方式达到,但这个缺点会是需要重新架构元件,不仅麻烦、而且代码也不易读 (React 文件戏称为 wrapper hell)。因此, Hook 的解决的主要问题之一,正是让共享状态逻辑。

使用 Hooks,开发者可以从元件中提取状态相关的逻辑,并对它进行独立复用。我们也不需要重组元件,就可以在元件中复用相同逻辑。举例来说,如果某个应用程式需要侦测页面滑动,并根据滑动来触发某些函式,这时我们可以写一个 useScroll,并在不同页面共享这个 useScroll 的状态逻辑。目前在 React 的社群中,有很多 Hooks 的函式库,便提供了各式各样可重复使用的 Hooks。

在 Hooks 出现前,复杂元件的逻辑会越写越让人难理解

当 class component 越来越庞大或逻辑复杂时,我们可能会在同一个生命周期方法内需要加入很多不相关的逻辑,例如:componentDidMount 要处理 data fetching 和事件侦听器逻辑,不仅难以理解也不好维护,且在很多情况下,也无法把这些元件拆成更小的元件。

为了解决此问题, Hooks 允许将一个元件拆为更小的函式,而不是根据生命周期的方法拆分,例如上段例子,data fetching 和事件侦听器两种逻辑,可以在同一个元件,通过两个useEffect 各自处理,又或者两者拆成两个 custom hooks。透过这样细分,,不相关的逻辑可以拆到不同的 useEffect,让副作用的逻辑更好管理。

class 对于开发者来说不好理解

React 团队发现,class 可能会是学习 React 的主要障碍。因为 class 的概念在 JavaScript 和其他语言中相当不同 (在 JavaScript 当中,class 是语法糖),如果开发者过去是写其他语言,转来写 JavaScript 时,需要特别了解 class 在 JavaScript 是如何运作的。

为了解决这些问题,Hooks 可以在没有 class 的情况下使用更多 React 的特性。从概念上讲,React 元件一直更接近函式。 Hooks 包含函式,但不牺牲 React 的实用精神,且不需要特别学习复杂的 functional or reactive programming。

Hook 的规则只能在最顶层使用 Hook

不能在for 循环、if…else 或巢状中(如: map )中使用 Hook,我们需要确保永远都在 React 函式的最顶层以及任何 return 之前使用(备注:在《为什么只能在最顶端层呼叫 Hook?》一文当中,我们有详细讨论原因,这也是常考的面试题,推荐大家多读读)。

只能在 React 函式中使用 Hook

无法在一般的 JavaScript 函式中使用 Hook,只有以下两种情境可以使用

在 React 的 functional component 中使用在自定义的 Hook (custom hook) 中使用其他 Hook常使用的 HookuseState:用于定义和保存元件中状态(state),会回传一个包含两个值的数组,第一个值是现在 state 的值,第二个值是一个 setter function,我们可以透过这个 setter function 更新 state 值并触发 re-render。useEffect:当想执行副作用(side effect)时,会透过 useEffect 处理,例如:fetch api、纪录追踪、setInterval()。我们需要传送两个参数在useEffect 中,第一是一个 setup function,代表我们想执行的副作用代码,如果最后我们要清除这个副作用,需要在最后回传一个 cleanup function; 第二个参数会是一个数组,数组中的元素会是相依于这个副作用代码会用到的变数。useLayoutEffect:与 useEffect 是类似的,唯一不同点在于执行时机不同,useLayoutEffect 会在 DOM 更新之后执行; useEffect 则是在 render 渲染结束后执行。useReducer:也是一种管理 state 的 hook,可作为 useState 替代方案。接收两个参数,第一个是 reducer,第二个为初始值。会返回两个值,现在 state 的值和 dispatch 方法。当状态管理逻辑变得更复杂时,通常会建议使用 useReducer 而非 useState。useCallback:在重新渲染之间,用来缓存函式的方法。会回传一个被 memoized 的 callback 函式,只有在依赖发生变化时,才会更新。此方法通常用在性能优化时。useMemo:在重新渲染之间,用来缓存计算结果的方法。传入一个创建函式和依赖项目,创建函式会需要返回一个值,只有在依赖发生变化时,才会更新值。此方法通常用在性能优化时。useRef:用来储存记录不需要渲染的值。会返回一个可变的 ref 物件,.current 属性会被初始化为传入的参数值(initialValue)。

Copyright © 2022 篮球世界杯_世界杯亚洲区名额 - cdbnfc.com All Rights Reserved.