React Hook

1. Hook 简介

Hook 可以让我们在不编写 class 的情况下使用 state 以及其他的 React 特性。

1.1 没有破坏性改动

Hook是:

  • 完全可选的。无需重写任何已有代码就可以在一些组件中尝试Hook。
  • 100%向后兼容。Hook不包含任何破坏性改动。

使用Hook 的原因:

  • 可以使用 Hook 从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使你在无需修改组件结构的情况下复用状态逻辑。 这使得在组件间或社区内共享 Hook 变得更便捷。

  • Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分。你还可以使用 reducer 来管理组件的内部状态,使其更加可预测。

  • Hook 使你在非 class 的情况下可以使用更多的 React 特性。 从概念上讲,React 组件一直更像是函数。而 Hook 则拥抱了函数,同时也没有牺牲 React 的精神原则。Hook 提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术。

1.2 渐进策略

没有计划从 React 中移除 class。

2. Hook 概览

Hook 是向下兼容的

Hook 是一些可以在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得不使用 class 也能使用 React。它们的名字通常以 use 开始。

2.1 Hook 使用规则
  • 只能在 函数最外层 调用 Hook。不要在循环、条件判断或者子函数中调用。
  • 只能在 React 的函数组件 中调用Hook。不要在其他 Js 函数 中调用。
  • 可以在自定义 Hook 中调用。
  • 提供了 linter 插件来自动执行这些规则。
3. useState Hook

useState 是允许在 React 函数组件中添加 state 的 Hook。

3.1 声明 State 变量

直接在组件中调用 useState Hook:

// 引入 useState Hook。它让我们在函数组件中存储内部 state
import React, { useState } from 'react';

function Example() {
    // 声明一个叫 “count” 的 state 变量
    const [count, setCount] = useState(0);
}
  • 调用 useState 方法的时候做了什么?

    定义了一个 “state变量“。 useState 是一种新的在函数调用时保存变量的方式,它与 class 里面的 this.state 提供的功能完全相同。一般来说,变量会在函数退出后”消失“,而 state 中的变量会被 React 保留。

  • useState 需要的参数

    **唯一参数为 初始 state,可按需赋值。**若想要在 state 中存储多个变量,只需调用 useState() 多次即可。

  • useState 方法的返回值

    **返回值:当前 state 以及更新 state 的函数。**返回一个有两个值的数组。

    const [count, setCount] = useState() 类似于 class组件里的this.state.count 和 this.setState

3.2 读取 State
// class 组件中,读取this.state.count
<p>You clicked {this.state.count} times></p>

// 函数组件中,直接使用 useState 中定义的 count
<p>You clicked {count} times></p>
3.3 更新 State
// class 组件中,调用this.setState() 更新 count 值,更新变量时会合并旧值于新值
 <button onClick={() => this.setState({ count: this.state.count + 1 })}>
    Click me
  </button>

// 函数组件中, 使用setCount 更新 count值,更新变量时总是替换它而不是合并它
<button onClick={() => setCount(count + 1)}>
    Click me
  </button>
3.4 使用多个 state 变量

useState 允许我们给不同的 state 变量取不同的名称,并且可以单独更新它们:

function ExampleWithManyStates() {
  // 声明多个 state 变量
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: '学习 Hook' }]);
4. useEffect Hook

Effect Hook 可以在函数组件中执行副作用操作。

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

function EXample() {
    const [count, setCount] = useState(0);
    
    useEffect(() => {
        document.title ='You clicked ${count} times';
    });
    
    retun(
      <div>
        <p>You clicked {count} times</p>
        <button onClick={() => setCount(count + 1)}>
          Click me
        </button>
      </div>
    )
}

副作用:数据获取,设置订阅以及动手更改 React 组件中的 DOM 都属于副作用。

React组件中常见的两种副作用操作为:需要清除和不需要清除的。

4.1 无需清除的 effect

有时候,我们只想**在 React 更新 DOM 之后运行一些额外的代码。**比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清除的操作。因为我们在执行完这些操作之后,就可以忽略他们了。

  • useEffect 做了什么?

    useEffect 告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。

  • 调用 useEffect 的原因

    将 useEffect 放在组件内部让我们可以在 effect 中直接访问 state 变量(或其他 props)。

    无需特殊的API读取它——它已经保存在函数作用域中。Hook 使用了 js 闭包机制,不用在 js 已经提供了解决方案的情况下,还引入特定的React API。

  • useEffect 会在每次渲染后都执行

    默认情况下,useEffect 在第一次渲染和每次更新之后都会执行。即在每次渲染后都执行。

    React 保证了每次运行 effect 的同时,DOM都已经更新完毕。

  • 提示

    componentDidMountcomponentDidUpdate 不同,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 useLayoutEffect Hook 供你使用,其 API 与 useEffect 相同。

4.2 需要清除的 effect

React 中一些副作用是需要清除的。例如订阅外部数据源。这种情况下,清除工作是非常重要的,可以防止引起内存泄露!

由于添加和删除订阅的代码的紧密性,所以 useEffect 的设计是在同一个地方执行。如果你的 effect 返回一个函数,React 将会在执行清除操作时调用它。

示例:

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    // Specify how to clean up after this effect:
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
  • 为何在 effect 中返回一个函数?这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。

  • React 何时清除 effect? React 会在组件卸载的时候执行清除操作。正如之前学到的,effect 在每次渲染的时候都会执行。这就是为什么 React 在执行当前 effect 之前对上一个 effect 进行清除。

  • 注意

    并不是必须为 effect 中返回的函数命名。这里我们将其命名为 cleanup 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给起一个别的名字。

4.3 使用多个 Effect 实现关注点分离
  • Hook 允许我们按照代码的用途分离他们,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的每一个effect。
4.4 为什么每次更新的时候都要运行 Effect
  • 此默认行为保证了一致性,避免了在 class 组件中因为没有处理更新逻辑而导致常见的 bug。
4.5 通过跳过 Effect 进行性能优化
  • 如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可:

    useEffect(() => {
      document.title = `You clicked ${count} times`;
    }, [count]); // 仅在 count 更改时更新
    
  • 如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect。

  • 注意:

    如果你要使用此优化方式,请确保数组中包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量,否则你的代码会引用到先前渲染中的旧变量。参阅文档,了解更多关于如何处理函数以及数组频繁变化时的措施内容。

    如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不属于特殊情况 —— 它依然遵循依赖数组的工作方式。

    如果你传入了一个空数组([]),effect 内部的 props 和 state 就会一直拥有其初始值。尽管传入 [] 作为第二个参数更接近大家更熟悉的 componentDidMountcomponentWillUnmount 思维模式,但我们有更好的方式来避免过于频繁的重复调用 effect。除此之外,请记得 React 会等待浏览器完成画面渲染之后才会延迟调用 useEffect,因此会使得额外操作很方便。

    我们推荐启用 eslint-plugin-react-hooks 中的 exhaustive-deps 规则。此规则会在添加错误依赖时发出警告并给出修复建议。

5. Hook 规则
5.1 只在最顶层使用 Hook

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useStateuseEffect 调用之间保持 hook 状态的正确。

5.2 只在 React 函数中调用 Hook

不要在普通的 JavaScript 函数中调用 Hook。我们可以:

  • ✅ 在 React 的函数组件中调用 Hook
  • ✅ 在自定义 Hook 中调用其他 Hook
6. 自定义 Hook

通过自定义 Hook ,可以将组件逻辑提取到可重用的函数中。

自定义 Hook 是一个函数,其名称以“use” 开头,函数内部可以调用其他的 Hook

  • 确保只在自定义 Hook 的顶层无条件地调用其他 Hook。

  • 可以自由决定自定义 Hook 的参数及其返回值。

  • 自定义 Hook 可看作以 “use” 开头命名的 普通 Js 函数。

  • 自定义 Hook 是一种自然遵循 Hook 设计的约定, 而并不是 React 的特性。

  • **自定义 Hook 必须以“use" 开头。**不遵循的话,由于无法判断某个函数是否包含对其内部 Hook 的调用,React 将无法自动检查你的 Hook 是否违反了 Hook 的规则。

  • **在两个组件中使用相同的 Hook 不会共享 state。**自定义 Hook 是一种重用状态逻辑的机制,所以每次使用自定义 Hook 时,其中的所有 state 和 副作用都是完全隔离的。

  • **自定义 Hook 如何获取独立的 state? ** 每次调用 Hook,它都会获取独立的 state。

  • 在多个 Hook 之间传递信息。由于 Hook 本身就是函数,因此我们可以在它们之间传递信息。

7. Hook API 索引
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值