React Hooks 原理入门基础_组件小白攻略

我们大部分 React 类组件可以保存状态,而函数组件不能? 并且类组件具有生命周期,而函数组件却不能?React 早期版本,类组件可以通过继承PureComponent来优化一些不必要的渲染,相对于函数组件,React 官网没有提供对应的方法来缓存函数组件以减少一些不必要的渲染,直接 16.6 出来的 React.memo函数。React 16.8 新出来

React Hooks 原理入门基础

我们大部分 React 类组件可以保存状态,而函数组件不能? 并且类组件具有生命周期,而函数组件却不能?

React Hooks 原理入门基础_组件小白攻略

React 早期版本,类组件可以通过继承PureComponent来优化一些不必要的渲染,相对于函数组件,React 官网没有提供对应的方法来缓存函数组件以减少一些不必要的渲染,直接 16.6 出来的 React.memo函数。

React 16.8 新出来的Hook可以让React 函数组件具有状态,并提供类似 componentDidMount和componentDidUpdate等生命周期方法。

类被会替代吗?

Hooks不会替换类,它们只是一个你可以使用的新工具。React 团队表示他们没有计划在React中弃用类,所以如果你想继续使用它们,可以继续用。

我能体会那种总有新东西要学的感觉有多痛苦,不会就感觉咱们总是落后一样。Hooks 可以当作一个很好的新特性来使用。当然没有必要用 Hook 来重构原来的代码, React团队也建议不要这样做。

Go Go

来看看Hooks的例子,咱们先从最熟悉的开始:函数组件。

以下 OneTimeButton 是函数组件,所做的事情就是当我们点击的时候调用 sayHi 方法。

import React from 'react';
import { render } from 'react-dom';

function OneTimeButton(props) {
  return (
    <button onClick={props.onClick}>
        点我点我
    </button>
  )
}

function sayHi() {
  console.log('yo')
}

render(
  <OneTimeButton onClick={sayHi}/>,
  document.querySelector('#root')
)

我们想让这个组件做的是,跟踪它是否被点击,如果被点击了,禁用按钮,就像一次性开关一样。

但它需要一个state,因为是一个函数,它不可能有状态(React 16.8之前),所以需要重构成类。

函数组件转换为类组件的过程中大概有5个阶段:

*否认:也许它不需要是一个类,我们可以把 state 放到其它地方。

  • 实现: 废话,必须把它变成一个class,不是吗?
  • 接受:好吧,我会改的。
  • 努力加班重写:首先 写 class Thing extends React.Component,然后 实现 render等等 。
  • 最后:添加state。
class OneTimeButton extends React.Component {
  state = {
    clicked: false
  }

  handleClick = () => {
    this.props.onClick();

    // Ok, no more clicking.
    this.setState({ clicked: true });
  }

  render() {
    return (
      <button
        onClick={this.handleClick}
        disabled={this.state.clicked}
      >
        You Can Only Click Me Once
      </button>
    );
  }
}

这是相当多的代码,组件的结构也发生了很大的变化, 我们需要多个小的功能,就需要改写很多。

使用 Hook 轻松添加 State

接下来,使用新的 useState hook向普通函数组件添加状态:

import React, { useState } from 'react'

function OneTimeButton(props) {
  const [clicked, setClicked] = useState(false)
  
  function doClick() {
    props.onClick();
    setClicked(true)
  }

  return (
    <button
      onClick={clicked ? undefined : doClick}
      disabled={clicked}
    >
      点我点我
    </button>
  )
}

这段代码是如何工作的

这段代码的大部分看起来像我们一分钟前写的普通函数组件,除了useState。

useState是一个hook。 它的名字以“use”开头(这是Hooks的规则之一 – 它们的名字必须以“use”开头)。

useState hook 的参数是 state 的初始值,返回一个包含两个元素的数组:当前state和一个用于更改state 的函数。

类组件有一个大的state对象,一个函数this.setState一次改变整个state对象。

函数组件根本没有状态,但useState hook允许我们在需要时添加很小的状态块。 因此,如果只需要一个布尔值,我们就可以创建一些状态来保存它。

由于Hook以某种特殊方式创建这些状态,并且在函数组件内也没有像setState函数来更改状态,因此 Hook 需要一个函数来更新每个状态。 所以 useState 返回是一对对应关系:一个值,一个更新该值函数。 当然,值可以是任何东西 – 任何JS类型 – 数字,布尔值,对象,数组等。

现在,你应该有很多疑问,如:

  • 当组件重新渲染时,每次都不会重新创建新的状态吗? React如何知道旧状态是什么?
  • 为什么hook 名称必须以“use”开头? 这看起来很可疑。
  • 如果这是一个命名规则,那是否意味着我可以自定义 Hook。
  • 如何存储更复杂的状态,很多场景不单单只有一个状态值这么简单。

Hooks 的魔力

将有状态信息存储在看似无状态的函数组件中,这是一个奇怪的悖论。这是第一个关于钩子的问题,咱们必须弄清楚它们是如何工作的。

原作者得的第一个猜测是某种编译器的在背后操众。搜索代码useWhatever并以某种方式用有状态逻辑替换它。

然后再听说了调用顺序规则(它们每次必须以相同的顺序调用),这让我更加困惑。这就是它的工作原理。

React第一次渲染函数组件时,它同时会创建一个对象与之共存,该对象是该组件实例的定制对象,而不是全局对象。只要组件存在于DOM中,这个组件的对象就会一直存在。

使用该对象,React可以跟踪属于组件的各种元数据位。

请记住,React组件甚至函数组件都从未进行过自渲染。它们不直接返回HTML。组件依赖于React在适当的时候调用它们,它们返回的对象结构React可以转换为DOM节点。

React有能力在调用每个组件之前做一些设置,这就是它设置这个状态的时候。

其中做的一件事设置 Hooks 数组。 它开始是空的, 每次调用一个hook时,React 都会向该数组添加该 hook。

为什么顺序很重要

假设咱们有以下这个组件:

function AudioPlayer() {
  const [volume, setVolume] = useState(80);
  const [position, setPosition] = useState(0);
  const [isPlaying, setPlaying] = useState(false);

  .....
}

因为它调用useState 3次,React 会在第一次渲染时将这三个 hook 放入 Hooks 数组中。

下次渲染时,同样的3个hooks以相同的顺序被调用,所以React可以查看它的数组,并发现已经在位置0有一个useStatehook ,所以React不会创建一个新状态,而是返回现有状态。

这就是React能够在多个函数调用中创建和维护状态的方式,即使变量本身每次都超出作用域。

多个useState 调用示例

让咱们更详细地看看这是如何实现的,第一次渲染:

  1. React 创建组件时,它还没有调用函数。React 创建元数据对象和Hooks的空数组。假设这个对象有一个名为nextHook的属性,它被放到索引为0的位置上,运行的第一个hook将占用位置0。
  2. React 调用你的组件(这意味着它知道存储hooks的元数据对象)。
  3. 调用useState,React创建一个新的状态,将它放在hooks数组的第0位,并返回[volume,setVolume]对,并将volume 设置为其初始值80,它还将nextHook索引递增1。
  4. 再次调用useState,React查看数组的第1位,看到它是空的,并创建一个新的状态。 然后它将nextHook索引递增为2,并返回[position,setPosition]。
  5. 第三次调用useState。 React看到位置2为空,同样创建新状态,将nextHook递增到3,并返回[isPlaying,setPlaying]。

现在,hooks 数组中有3个hook,渲染完成。 下一次渲染会发生什么?

  1. React需要重新渲染组件, 由于 React 之前已经看过这个组件,它已经有了元数据关联。
  2. React将nextHook索引重置为0,并调用组件。
  3. 调用useState,React查看索引0处的hooks数组,并发现它已经在该槽中有一个hook。,所以无需重新创建一个,它将nextHook推进到索引1并返回[volume,setVolume],其中volume仍设置为80。
  4. 再次调用useState。 这次,nextHook为1,所以React检查数组的索引1。同样,hook 已经存在,所以它递增nextHook并返回[position,setPosition]。
  5. 第三次调用useState,我想你知道现在发生了什么。

就是这样了,知道了原理,看起来也就不那么神奇了, 但它确实依赖于一些规则,所以才有使用 Hooks 规则。

Hooks 的规则

自定义 hooks 函数只需要遵守规则 3 :它们的名称必须以“use”为前缀。

例如,我们可以从AudioPlayer组件中将3个状态提取到自己的自定义钩子中:

function AudioPlayer() {
  // Extract these 3 pieces of state:
  const [volume, setVolume] = useState(80);
  const [position, setPosition] = useState(0);
  const [isPlaying, setPlaying] = useState(false);

  // < beautiful audio player goes here >
}

因此,咱们可以创建一个专门处理这些状态的新函数,并使用一些额外的方法返回一个对象,以便更容易启动和停止播放,例如:

function usePlayerState(lengthOfClip) {
  const [volume, setVolume] = useState(80);
  const [position, setPosition] = useState(0);
  const [isPlaying, setPlaying] = useState(false);

  const stop = () => {
    setPlaying(false);
    setPosition(0);
  }

  const start = () => {
    setPlaying(true);
  }

  return {
    volume,
    position,
    isPlaying,
    setVolume,
    setPosition,
    start,
    stop
  };
}

像这样提取状态的一个好处是可以将相关的逻辑和行为组合在一起。可以提取一组状态和相关事件处理程序以及其他更新逻辑,这不仅可以清理组件代码,还可以使这些逻辑和行为可重用。

另外,通过在自定义hooks中调用自定义hooks,可以将hooks组合在一起。hooks只是函数,当然,函数可以调用其他函数。

总结

Hooks 提供了一种新的方式来处理React中的问题,其中的思想是很有意思且新奇的。

React团队整合了一组很棒的文档和一个常见问题解答,从是否需要重写所有的类组件到钩Hooks是否因为在渲染中创建函数而变慢? 以及两者之间的所有东西,所以一定要看看。

海计划公众号
(0)
上一篇 2020/03/26 23:29
下一篇 2020/03/26 23:28

您可能感兴趣的内容

  • 微信和支付宝中的一些常用方法封装入门基础知识_封装小白知识

    最近做了同一个样子的小程序的支付宝版本,现在如果想想是开发两次的话心里应该是很难受的。去年11月12月份左右开发了两个微信小程序是一个在超市买商品的,一个用户版本,一个商户版本的。我们团队看到了uniapp这个东西,然后用这个东西写了一点demo,然后就决定采纳这个东西开发微信小程序了。这个开发体验真的是让人揪心,因为编译起来太慢了,就是这边保存完代码,你如

    2020/03/24
  • 弹性(Flex)布局的使用小白攻略_布局使用攻略

    最近我参与实施的两个项目中,一个页面交互复杂,而另一个相对传统,两个项目相比之下凸显出了页面布局样式的时间占比不可忽视,使用了弹性布局代码量精简了不少。虽说如此,弹性布局往往会有些潜在的问题,且改动后,要立即查看页面也需要不少时间,因此我把项目中使用弹性布局过程中遇到的问题稍作整理,为大家以后使用时,可以有效规避这些麻烦。 使用场景前端开发中,网页布局是很重

    2020/03/20
  • Layabox小白指南Layabox是免费开源的HTML5引擎解决方案

    Layabox基础入门 官方网址:https://www.layabox.com/ 简介描述:Layabox是免费开源的HTML5引擎解决方案 LayaBox是中国领先的游戏引擎提…

    2020/03/05
  • JS算法题之盛最多水的容器教程视频_算法题使用说明

    题目描述给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。说明:你不能倾斜容器,且 n 的值至少为 2。图中垂直线代表输入数组 [1,8,6,2,5,4,8,3

    2020/03/29
  • js栈和堆的区别菜鸟教程_区别入门指南

    一、 堆(heap)和栈(stack)栈(stack)会自动分配内存空间,会自动释放。堆(heap)动态分配的内存,大小不定也不会自动释放。二、 基本类型和引用类型基本类型:简单的数据段,存放在栈内存中,占据固定大小的空间。引用类型:指那些可能由多个值构成的对象,保存在堆内存中,包含引用类型的变量实际上保存的不是变量本身,二十指向该对象的指针。基本数据类

    2020/03/22
  • 浏览器获取手机经纬度位置指南攻略_定位基础教程

    经纬度位置无法获取的情况:1. 网址必须为域名,不能用ip直接访问,否则手机浏览器直接拒绝改请求。2. iphone的浏览器(包括微信扫码进入网站) ,亲测调用 http 的网站是不能获取到经纬度的,是苹果手机安全的考虑,如果使用https网站的话,证书也必须是有效证书(无效证书的时候浏览器访问会提示该网站不安全是否继续访问,即使“继续访问”也不能获取经纬度

    2020/03/24
  • vuera基础知识_无缝集成 React 和 Vue

    vuera基础知识 GitHub:https://github.com/akxcv/vuera 简介描述:无缝集成 React 和 Vue vuera缝集成 React 和 Vue…

    2020/03/06
  • Js继承总结使用教程_继承入门百科

    原型链当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。如果让原型对象指向另一个类型的实例…..有趣的事情便发生了.即: Person.prototype = animal2鉴于上述游戏规则生效,如果试图引用Person构造的实例person1的某个属性:1).首先会在instance1

    2020/03/24
  • 怎么判定web前端架构师的能力高低?小白知识_架构基础入门

    软件架构(software architecture)是一系列相关的抽象模式,用于指导大型软件系统各个方面的设计。传统软件架构描述的对象是直接构成系统的抽象组件,侧重于系统的抽象、拆分、组织方式等。所以如果从传统软件架构定义出发,前端架构可能就是指前端项目的系统设计了。在进行系统设计之前,由于前端开发语言缺乏一定的工程能力,所以web前端架构师相比传统软件架

    2020/04/03
  • form表单提交数据的几种方式菜鸟教程网_form使用帮助

    一、submit提交一般表单提交通过type=submit实现,input type=”submit”,浏览器显示为button按钮,通过点击这个按钮提交表单数据跳转到/url.do <input type='text' name='user

    2020/03/20
  • vue使用slot分发内容与react使用prop分发内容基础知识_prop使用帮助

    vue将 元素作为承载分发内容的出口// layout.vue

    当组件渲染的时候, 将会被替换该组件起始标签和结束标签之间的任何内容// hone.vue
    <l

    2020/03/24
  • Javascript中的object相等菜鸟知识_object入门教程

    原文出处:Object Equality in JavaScript相等是JavaScript中起初最让人困惑的部分。==和===的比较、强制类型的顺序等等,都使得这个问题变得复杂。今天,我们会聚焦另一个方面:object相等是如何实现的。你也许认为,如果两个object有相同的属性并且它们多有的属性值都相同,那么这两个object应该是相等的。我们来看看是

    2020/03/31
  • qunit菜鸟指南_一个强大的JavaScript单元测试框架

    qunit菜鸟指南 官方网址:https://qunitjs.com GitHub:https://github.com/qunitjs/qunit 简介描述:一个强大的JavaS…

    2020/03/06
  • Bojler小白攻略_一个电子邮件模板和HTML代码编写指南

    Bojler小白攻略 官方网址:https://bojler.slicejack.com GitHub:https://github.com/Slicejack/bojler 简介…

    2020/03/07
  • 20年程序员分享编程经验新手入门_经验攻略教程

    从11岁时,我就一直在编程,并且一直都很喜欢技术和编程。这些年来,我积累了一些艰难又容易的经验。作为一名程序员,你或许还没这些经验,但我会把它们献给那些想从中学到更多的朋友。我会持续更新这些经验,我可能还会有更多的感想,但就我这20年来看,我想下面这个列表中基本不需要增添额外的东西了。下面就是我至今最难忘的经验。1. 估算解决问题所需要的时间。不要怕,承认吧

    2020/03/29
  • IE9及以下浏览器升级提示入门基础教程_浏览器菜鸟教程

    在使用react的项目中一般都有兼容性问题,特别是使用了组件库比如element-react或者ant-design等等,在ie下多少都会有点小问题,比如样式不正确,或者组件功能失效,甚至白屏等问题。IE白屏问题一般情况下,白屏的问题在index.html中引入这两个js就能解决
    <script src="./es6-s

    2020/03/22