Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

为什么会有hook

  • 在组件之间复用状态逻辑很难,需要重新组织你的组件结构,抽象层组成的组件会形成“嵌套地狱”
  • 复杂组件变得难以理解,各生命周期交叉副作用

State Hook

import React, { useState } from 'react';        // 引入

function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0); return (
<div>
<p>You clicked {count} times</p> // 使用
<button onClick={() => setCount(count + 1)}> // 改变
Click me
</button>
</div>
);
}

Hook 在 class 内部是不起作用的。但你可以使用它们来取代 class 。

useState 需要哪些参数?

useState() 方法里面唯一的参数就是初始 state。不同于 class 的是,我们可以按照需要使用数字或字符串对其进行赋值,而不一定是对象。在示例中,只需使用数字来记录用户点击次数,所以我们传了 0 作为变量的初始 state。(如果我们想要在 state 中存储两个不同的变量,只需调用 useState() 两次即可。)

useState 方法的返回值是什么?

返回值为:当前 state 以及更新 state 的函数。这就是我们写 const [count, setCount] = useState() 的原因。这与 class 里面 this.state.count 和 this.setState 类似,唯一区别就是你需要成对的获取它们。

我们声明了一个叫 count 的 state 变量,然后把它设为 0。React 会在重复渲染时记住它当前的值,并且提供最新的值给我们的函数。我们可以通过调用 setCount 来更新当前的 count

读取 State

当我们想在 class 中显示当前的 count,我们读取 this.state.count:

<p>You clicked {this.state.count} times</p>

在函数中,我们可以直接用 count:

<p>You clicked {count} times</p>

更新 State

在 class 中,我们需要调用 this.setState() 来更新 count 值:

<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>

在函数中,我们已经有了 setCount 和 count 变量,所以我们不需要 this:

<button onClick={() => setCount(count + 1)}>
Click me
</button>

使用多个 state 变量

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

在以上组件中,我们有局部变量 age,fruit 和 todos,并且我们可以单独更新它们:

function handleOrangeClick() {
// 和 this.setState({ fruit: 'orange' }) 类似
setFruit('orange');
}

Effect Hook

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

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

function Example() {
const [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
}); return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

使用 class 的示例

class Example extends React.Component {
state = {
count: 0
} componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
} componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
} render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}

注意,在这个 class 中,我们需要在两个生命周期函数中编写重复的代码。

使用 Hook 的示例

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

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

为什么在组件内部调用 useEffect?

将 useEffect 放在组件内部让我们可以在 effect 中直接访问 count state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经保存函数作用域中。Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。

useEffect 会在每次渲染后都执行吗?

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

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

需要清除的 effect

在 React class 中,你通常会在 componentDidMount 中设置订阅,并在 componentWillUnmount清除它。

使用 Hook 的示例

你可能认为需要单独的 effect 来执行清除操作。但由于添加和删除订阅的代码的紧密性,所以 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 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给起一个别的名字。

React.useEffect(() => {
const handler = () => {
const width = `calc(100% - 80)`;
setWidth(width);
};
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, [1]);

提示: 通过跳过 Effect 进行性能优化

在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在 class 组件中,我们可以通过在 componentDidUpdate 中添加对 prevProps 或 prevState 的比较逻辑解决:

如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可:

useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新

如果 count 的值是 5,而且我们的组件重渲染的时候 count 还是等于 5,React 将对前一次渲染的 [5] 和后一次渲染的 [5] 进行比较。因为数组中的所有元素都是相等的(5 === 5),React 会跳过这个 effect,这就实现了性能的优化。

对于有清除操作的 effect 同样适用:

useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
} ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}, [props.friend.id]); // 仅在 props.friend.id 发生变化时,重新订阅

如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])

Hook 规则

  • 只在最顶层使用 Hook

    (不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。)
  • 只在 React 函数中调用 Hook
 // 												

React使用hook的更多相关文章

  1. [React] Write a stateful Component with the React useState Hook and TypeScript

    Here we refactor a React TypeScript class component to a function component with a useState hook and ...

  2. [React] Use the React Effect Hook in Function Components

    Similar to the State Hook, the Effect Hook is “first-class” in React and handy for performing side e ...

  3. [React] Create a Persistent Reference to a Value Using React useRef Hook

    The useRef is a hook for creating values that persist across renders. In this lesson we'll learn how ...

  4. [React] Write a Custom React Effect Hook

    Similar to writing a custom State Hook, we’ll write our own Effect Hook called useStarWarsQuote, whi ...

  5. 使用React Hook后的一些体会

    一.前言 距离React Hook发布已经有一段时间了,笔者在之前也一直在等待机会来尝试一下Hook,这个尝试不是像文档中介绍的可以先在已有项目中的小组件和新组件上尝试,而是尝试用Hook的方式构建整 ...

  6. 关于为什么使用React新特性Hook的一些实践与浅见

    前言 关于Hook的定义官方文档是这么说的: Hook 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 简单来说,就是在 ...

  7. React 顶层 API

    概览 组件 使用 React 组件可以将 UI 拆分为独立且复用的代码片段,每部分都可独立维护.你可以通过子类 React.Component 或 React.PureComponent 来定义 Re ...

  8. 在React中使用 react-router-dom 编程式路由导航的正确姿势【含V5.x、V6.x】

    ## react-router-dom 编程式路由导航 (v5) ###### 1.push跳转+携带params参数 ```jsx props.history.push(`/b/child1/${i ...

  9. React MobX 开始

    MobX 用于状态管理,简单高效.本文将于 React 上介绍如何开始,包括了: 了解 MobX 概念 从零准备 React 应用 MobX React.FC 写法 MobX React.Compon ...

随机推荐

  1. 解决Macbook Pro 2017安装Windows10双系统后在Windows系统中Apple蓝牙鼠标不能使用问题

    MAC BOOK PRO 2017安装Windows10双系统 在Windows系统中蓝牙鼠标不能使用解决办法 最近因工作需要,macOS系统不能要求,大部分工作必须要在Windows系统中进行,最初 ...

  2. EVE模拟器的配置

    (注:本文整理自达叔的EVE模拟器使用说明https://blog.51cto.com/dashu666/1971728) 基础部署篇 所需要准备的东西: 1.VMWare (虚拟化软件,用来承载模拟 ...

  3. [Qt] 通过socket将另一个程序的某个窗口调到最前端

    @ // THIS IS A HACK: // from QT documentation: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ...

  4. html+css的用户注册界面

    注册界面样图 代码实现 html部分 <!DOCTYPE html> <html lang="en"> <head> <meta char ...

  5. 谷歌提高Google Assistant中Voice Match的准确性

    谷歌正在提高 Google Assistant 中 Voice Match 的准确性,使其变得更加完善.谷歌表示一旦用户在 Google Assistant 中启用 Voice Match 功能,那么 ...

  6. 【Linux网络基础】上网原理流程

    1. 局域网用户上网原理 上网过程说明: 确保物理设备和线路架构准备完毕,并且线路通讯状态良好 终端设备需要获取或配置上局域网(私有地址)地址,作为局域网网络标识 当终端设备想上网时,首先确认访问的地 ...

  7. 【Linux常见命令】uname命令

    uname命令用于显示系统信息. uname可显示电脑以及操作系统的相关信息. 语法 uname [-amnrsv][--help][--version] 参数说明: -a或--all 显示全部的信息 ...

  8. 【Linux常见命令】cp命令

    cp - copy files and directories 拷贝文件或目标文件夹,默认不能直接拷贝目录,通过-r参数设置递归复制目录 copy 语法: cp [OPTION]... [-T] SO ...

  9. 写给Java程序员的Java虚拟机学习指南

    大家好,我是极客时间<深入拆解Java虚拟机>作者.Oracle Labs高级研究员郑雨迪.有幸借这个专题的机会,能和大家分享为何Java工程师要学Java虚拟机?如何掌握Java虚拟机? ...

  10. postman(断言)

    一.断言 1.Code is 200 断言状态码是200 2.contains string 断言respoonse body中包含string 3.json value check (检查JSON值 ...