用动画和实战打开 React Hooks(一):useState 和 useEffect

在这里插入图片描述

我们研发开源了一款基于 Git 进行技术实战教程写作的工具,我们图雀社区的所有教程都是用这款工具写作而成,欢迎 Star

如果你想快速了解如何使用,欢迎阅读我们的 教程文档

本文由图雀社区成员 mRc 写作而成,欢迎加入图雀社区,一起创作精彩的免费技术教程,予力编程行业发展。

如果您觉得我们写得还不错,记得 点赞 + 关注 + 评论 三连,鼓励我们写出更好的教程💪

自从 React 16.8 发布之后,它带来的 React Hooks 在前端圈引起了一场无法逆转的风暴。React Hooks 为函数式组件提供了无限的功能,解决了类组件很多的固有缺陷。这篇教程将带你快速熟悉并掌握最常用的两个 Hook:useStateuseEffect。在了解如何使用的同时,还能管窥背后的原理,顺便实现一个 COVID-19(新冠肺炎)可视化应用。

欢迎访问本项目的 GitHub 仓库Gitee 仓库

起步

前提条件

在阅读这篇教程之前,希望你已经做了如下准备:

为什么会有 Hooks?

在 Hooks 出现之前,类组件和函数组件的分工一般是这样的:

  • 类组件提供了完整的状态管理和生命周期控制,通常用来承接复杂的业务逻辑,被称为“聪明组件
  • 函数组件则是纯粹的从数据到视图的映射,对状态毫无感知,因此通常被称为“傻瓜组件

有些团队还制定了这样的 React 组件开发约定:

有状态的组件没有渲染,有渲染的组件没有状态。

那么 Hooks 的出现又是为了解决什么问题呢?我们可以试图总结一下类组件颇具代表性的痛点

  1. 令人头疼的 this 管理,容易引入难以追踪的 Bug
  2. 生命周期的划分并不符合“内聚性”原则,例如 setIntervalclearInterval 这种具有强关联的逻辑被拆分在不同的生命周期方法中
  3. 组件复用(数据共享或功能复用)的困局,从早期的 Mixin,到高阶组件(HOC),再到 Render Props,始终没有一个清晰直观又便于维护的组件复用方案

没错,随着 Hooks 的推出,这些痛点都成为了历史!

为什么要写这一系列 Hooks 教程?

如何快速学习并掌握 React Hooks 一直是困扰很多新手或者老玩家的一个问题,而笔者在日常的学习和开发中也发现了以下头疼之处:

  • React 官方文档对 Hooks 的讲解偏应用,对原理的阐述一笔带过
  • 讲 React Hooks 的优秀文章很多,但大多专注于讲解一两个 Hook,要想一网打尽有难度
  • 看了很多使用方法甚至源码分析,但是没法和具体的使用场景对应起来,不了解怎么在实际开发中灵活运用

如果你也有同样的困惑,希望这一系列文章能帮助你拨开云雾,让 Hooks 成为你的称手兵器。我们将通过一个完整的 COVID-19 数据可视化项目,结合 Hooks 的动画原理讲解,让你真正地精通 React Hooks!

说实话,Hooks 的知识点相当分散,就像游乐园的游玩项目一样,选择一条完美的路线很难。但是不管怎么样,希望在接下来的旅程中,你能玩得开心😊!

初始化项目

首先,通过 Create React App(以下简称 CRA) 初始化项目:

npx create-react-app covid-19-with-hooks

在少许等待之后,进入项目。

提示

我们所有的数据源自 NovelCOVID 19 API,可以点击访问其全部的 API 文档。

一切就绪,让我们出发吧!

useState + useEffect:初来乍到

首先,让我们从最最最常用的两个 Hooks 说起:useStateuseEffect 。很有可能,你在平时的学习和开发中已经接触并使用过了(当然如果你刚开始学也没关系啦)。不过在此之前,我们先熟悉一下 React 函数式组件的运行过程。

理解函数式组件的运行过程

我们知道,Hooks 只能用于 React 函数式组件。因此理解函数式组件的运行过程对掌握 Hooks 中许多重要的特性很关键,请看下图:

可以看到,函数式组件严格遵循 UI = render(data) 的模式。当我们第一次调用组件函数时,触发初次渲染;然后随着 props 的改变,便会重新调用该组件函数,触发重渲染

你也许会纳闷,动画里面为啥要并排画三个一样的组件呢?因为我想通过这种方式直观地阐述函数式组件的一个重要思想:

每一次渲染都是完全独立的。

后面我们将沿用这样的风格,并一步步地介绍 Hook 在函数式组件中扮演怎样的角色。

useState 使用浅析

首先我们来简单地了解一下 useState 钩子的使用,官方文档介绍的使用方法如下:

const [state, setState] = useState(initialValue);

其中 state 就是一个状态变量,setState 是一个用于修改状态的 Setter 函数,而 initialValue 则是状态的初始值。

光看代码可能有点抽象,请看下面的动画:

与之前的纯函数式组件相比,我们引入了 useState 这个钩子,瞬间就打破了之前 UI = render(data) 的安静画面——函数组件居然可以从组件之外把状态和修改状态的函数“钩”过来!并且仔细看上面的动画,通过调用 Setter 函数,居然还可以直接触发组件的重渲染

提示

你也许注意到了所有的“钩子”都指向了一个绿色的问号,我们会在下面详细地分析那是什么,现在就暂时把它看作是组件之外可以访问的一个“神秘领域”。

结合上面的动画,我们可以得出一个重要的推论:每次渲染具有独立的状态值(毕竟每次渲染都是完全独立的嘛)。也就是说,每个函数中的 state 变量只是一个简单的常量,每次渲染时从钩子中获取到的常量,并没有附着数据绑定之类的神奇魔法。

这也就是老生常谈的 Capture Value 特性。可以看下面这段经典的计数器代码(来自 Dan 的这篇精彩的文章):

function Counter() {
   
  const [count, setCount] = useState(0);

  function handleAlertClick() {
   
    setTimeout(() => {
   
      alert('You clicked on: ' + count);
    }, 3000);
  }

  return (
    <div>
      <p>You clicked {
   count} times</p>
      <button onClick={
   () => setCount(count + 1)}>
        Click me
      </button>
      <button onClick={
   handleAlertClick}>
        Show alert
      </button>
    </div>
  );
}

实现了上面这个计数器后(也可以直接通过这个 Sandbox 进行体验),按如下步骤操作:1)点击 Click me 按钮,把数字增加到 3;2)点击 Show alert 按钮;3)在 setTimeout 触发之前点击 Click me,把数字增加到 5。

结果是 Alert 显示 3!

如果你觉得这个结果很正常,恭喜你已经理解了 Capture Value 的思想!如果你觉得匪夷所思嘛……来简单解释一下:

  • 每次渲染相互独立,因此每次渲染时组件中的状态、事件处理函数等等都是独立的,或者说只属于所在的那一次渲染
  • 我们在 count 为 3 的时候触发了 handleAlertClick 函数,这个函数所记住的 count 也为 3
  • 三秒种后,刚才函数的 setTimeout 结束,输出当时记住的结果:3

这道理就像,你翻开十年前的日记本,虽然是现在翻开的,但记录的仍然是十年前的时光。或者说,日记本 Capture 了那一段美好的回忆。

useEffect 使用浅析

你可能已经听说 useEffect 类似类组件中的生命周期方法。但是在开始学习 useEffect

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值