react Hook详解

今天抽空学习了react Hook的相关知识,于是整理成博客记录一下相关比较重要的知识点,如果有什么不对的地方欢迎指正,废话不多说,我们马上进入正题。

1.什么是Hook
官网上的解释是:Hook是React16.8的新增特性,它可以让你在不编写class的情况下使用state以及其他的React特性。我们知道在函数式声明的组件中是没有类声明组件中所用到的state和生命周期等等特性的,Hook帮助我们解决了这样一个问题,在函数式声明的组件中也可以使用state以及其他的React特性(如useEffect,useReducer,useContext等,具体用法可参阅官方文档)。

2.State Hook
2.1基本用法

如果你在编写函数组件的时候需要向其添加一些state,以前的做法是必须将其转化为class,现在你可以在现有的函数组件中使用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>
  );
}

这个段代码与下面的类组件写法类似:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
 
  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

这里我们声明了一个count的state变量,并把它初始化为0;useState方法用来定义state变量,它接收唯一的参数,也就是初试的state,接收的参数形式可以是对象、数字、字符串、数组等,useState方法返回当前的state以及更新state的函数,这就是我们写

const [count, setCount] = useState()
的原因,setCount提供给我们更新state的方法,每次点击按钮count的值加一,不同于类组件的this.setState方法,Hook的setState方法每次都是拿新的state直接去替换旧的state,而this.setState方法是把新的state和旧的state进行合并,这就是为什么我们在this.setState中不修改某些值时,我们不需要在this.setState中调用它,然后这个值依然存在于state中

当我们读取state的值时,直接读取变量count就可以了,而不需要像类组件一样this.state.count,同样调用更新的方法也不需要加this

这个段代码与下面的类组件写法类似:

的原因,这是ES6的解构赋值的写法,当然你也可以按照下面的写法:

const temp = useState(0);
const count = temp[0];
const setCount = temp[1];


不过显然第一个的写法更加简洁。 

2.2使用多个state变量

我们可以通过多次声明的方式来使用多个state的变量:

 const [age, setAge] = useState(20);
 const [name, setName] = useState('Gavell');
 const [todos, setTodos] = useState([{ text: '学习 Hook' }]);


由于state可以存储对象和数组,当我们在声明多个state变量时,我们也可以把相关的数据一起声明到一个对象或者数组中,而不用多次的声明

那么当我们声明多个state变量的时候,React怎么知道哪个state对应哪个useState呢?答案是 React 靠的是 Hook 调用的顺序。因为我们的示例中,Hook 的调用顺序在每次渲染中都是相同的,所以它能够正常工作

比如上面声明的三个state变量:

 

 const [age, setAge] = useState(20);
 const [name, setName] = useState('Gavell');
 const [todos, setTodos] = useState([{ text: '学习 Hook' }]);
 
// ------------
// 首次渲染
// ------------
useState(20)                       // 1. 使用 20  初始化变量名为 age 的 state
useState('Gavell')                 // 2. 使用 'Gavell' 初始化变量名为 name 的 state
useState([{ text: '学习 Hook' }])  //3.使用一个数组来初始化变量名为 todos 的state
 
 
// -------------
// 二次渲染
// -------------
useState(20)                               // 1. 读取变量名为 age 的 state(参数被忽略)
useState('Gavell')                         // 2. 读取变量名为 name 的 state (参数被忽略)
useState([{ text: '学习 Hook' }])          // 3. 读取变量名为 todos 的 state(参数被忽略)
 
// ...


只要Hook的调用顺序在多次渲染之间保持一直,React就能正确的将内部的state和对应的Hook进行关联,并且第二次渲染开始,Hook传入的参数将被忽略(因为那只是初始化的值)

也因为这样,Hook给我们制定了规则:

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

3.Effect Hook
你之前可能已经在 React 组件中执行过数据获取、订阅或者手动修改过 DOM。我们统一把这些操作称为“副作用”,或者简称为“作用”。useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API。

例如,下面这个组件在 React 更新 DOM 后会设置一个页面标题:

import React, { useState, useEffect } from 'react';
 
function Example() {
  const [count, setCount] = useState(0);
 
  // 相当于 componentDidMount 和 componentDidUpdate:
  useEffect(() => {
    // 使用浏览器的 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 时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数。由于副作用函数是在组件内声明的,所以它们可以访问到组件的 props 和 state。默认情况下,React 会在每次渲染后调用副作用函数 —— 包括第一次渲染的时候。

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

在React class中,我们通常在componentDidMount 中设置订阅,并在 componentWillUnmount 中清除它,例如,假设我们有一个 ChatAPI 模块,它允许我们订阅好友的在线状态。以下是我们如何使用 class 订阅和显示该状态:

class FriendStatus extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOnline: null };
    this.handleStatusChange = this.handleStatusChange.bind(this);
  }
 
  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  handleStatusChange(status) {
    this.setState({
      isOnline: status.isOnline
    });
  }
 
  render() {
    if (this.state.isOnline === null) {
      return 'Loading...';
    }
    return this.state.isOnline ? 'Online' : 'Offline';
  }
}


而当我们使用了Effect Hook之后,如果你的effect返回的是一个函数,React将会在组件卸载的时候执行这个函数,而我们就可以在这个返回的函数中进行清除的相关操作,effect在每次重新渲染组件的时候都会执行,所以React会在执行当前的effect之前对上一个effect进行清除:

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会很方便,但是也会造成性能的下降——每次都调用需要开销。如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可:

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


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

当渲染时,如果 count 的值更新成了 6,React 将会把前一次渲染时的数组 [5] 和这次渲染的数组 [6] 中的元素进行对比。这次因为 5 !== 6,React 就会再次调用 effect。如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect。

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

关于Hook的知识学习就到这里,更多其他的Hook,如useContext、useReducer、useRef等,可以通过查阅相关文档进行学习。

转载于:https://blog.csdn.net/qq_41056833/article/details/105817061

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值