React Hook概念总结

React Hook是React 16.8中的新特性。它可以让你在不编写class组件的情况下使用state以及其他React特性(增强了函数式组件的功能)。

本文是基于React官网中对于Hook这一特性介绍的总结,建议大家可以先移步官网查看相关内容。

1. state hook(状态钩子)

import React, { useState } from "react";

function Example() {
    const [count, setCount] = useState(0);
    const [age, setAge] = useState(26);
    const [obj, setObj] = useState([{
        text: "Learn hooks"
    }]);

    return (
        <div>
            <button onClick={() => setCount(count + 1)} >加一</button><br />
            <button onClick={setCount(prevCount => prevCount - 1)} >加一</button>
        </div>
    );
}

useState会返回一对值:当前状态,以及修改该状态的函数setXXX。入参则是状态的默认值(这个默认值会在后续的state重绘中被忽略,官网称之为惰性的初始值)。复杂初始值的构造也可以通过一个函数来完成,例如:

function Example() {
    ...

    const [count, setCount] = useState(() => {
        ...
        return initialCountState;
    });

    ...
}

通过调用setXXX函数,会接收一个XXX所要修改的新的state,并将组件的一次重新渲染加入队列中(由此可以看出是异步更新重绘机制)。
在后续的重新渲染中,useState返回的第一个值将始终是更新后的最新的状态值。与class组件中的setState不同,useState不会自动合并更新对象。

hook不能在class组件中使用,但是可以在函数式组件中使用。useState是系统内置的hook,你也可以自定义hook来复用不同组件间的状态逻辑。

 

2. Effect hook(带副作用的钩子)
useEffect是一个副作用的钩子,为函数式组件提供了操作副作用的能力(所谓的副作用可以理解为会引发某些状态改变的操作,例如发送请求获取数据,更新持久化数据,更新数据库等)。
我们可以通过useEffect来将之前耦合在某些生命周期函数(componentDidMount, componentDidUpdate, componentWillUnmount)中的带有副作用的不相关逻辑解耦出来,并复用之。例如:

function Example() {
    ...

    // 相当于componentDidMount和componentDidUpdate
    useEffect(() => {
        // 更新浏览器标题
        document.title = "You clicked ${count} times";
    });

    ...
}

声明在useEffect中的函数会在当前组件每次渲染(初次渲染及状态更新重绘)时,按序执行一遍。这也是为什么上面的代码相当于声明在componentDidMount和componentDidUpdate中。
而且由于useEffect函数是在函数组件内声明的,所以能够获取到当前组件的state和props对象。useEffect中传入的函数可以提供一个函数作为返回值,该函数会在重新调用effect前或组件销毁时(componentWillUnmount)执行,用于清除副作用。例如:

function Example() {
    ...

    useEffect(() => {
        subscribe(...);

        return () => {
            unsubscribe(...);
        }
    });
}

这样做的好处就是把关联的逻辑放到同一个useEffect钩子中,逻辑上更加清晰也便于管理和修改。相比于之前订阅函数和取消订阅函数必须要分散在componentDidMount和componentWillUnmount中执行而言。
至于为什么要在每次重新调用effect前执行清除上一次effect的函数,这是为了保证每次执行effect时,所关联的state或props都是最新的。
但是,每次重绘时,并不是所有的effect都需要重新执行,这可能带来相应的性能问题。因此,useEffect提供了第二个参数,这个参数是一个数组。在一次重绘中,如果某个effect中数组里所有的state都没有发生变化,那么该effect就不会在本次重绘中执行。例如:

function Example() {
    ...

    useEffect(() => {
        document.title = "You clicked ${count} times";
    }, [count]);

    ...
}

假如上一次effect执行时count是5,本次重绘触发时,count依旧是5,那么effct不会重新执行。同时,如果希望某个effect仅在组件挂载和卸载时执行,可以传入空数组,告知React当前effect不依赖于任何state或者是props。例如:

function Example() {
    ...

    useEffect(() => {
        document.title = "You clicked ${count} times";
    }, []);

    ...
}

只会在组件挂载时执行一次。

最后,对于条件式发起effect,一定要确保第二个参数数组中包括第一个参数函数内所引用的外部作用域的props或state,除非你明确的想要effect不随相应的props或state变化而执行。

条件数组中不仅仅可以包含变量,还可以传入外部函数的引用,这样可以确保effect能随着调用过的函数内使用的props或state的更新而执行,例如:

function Example({ someProp }) {
  function doSomething() {
    console.log(someProp);
  }

  useEffect(() => {
    doSomething();
  }, [doSomething]); 
}

 

 

3. hook使用规则
hook实际上就是JavaScript函数,但是使用时需要额外遵循以下两个原则:
(1)只能在React函数组件中使用hook,或者是自定义hook中;
(2)只能在函数的最外层调用hook,不要在循环、条件或者子函数中调用;
    这条规则是为了保证在每次重新渲染时,hook的调用顺序都是完全一致的,不会因为某个条件而导致执行顺序发生变化。React需要通过hook的执行顺序来保证内部state和对应hook关联的正确性,否则可能会产生bug。

 

4. 自定义hook与hook跨组件复用
当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。而组件和hook都是函数,所以也同样适用这种方式。
自定义hook可以理解为就是名称以"use"开头的函数,其内部可以调用其他hook函数,其自身也可以作为一个整体在React函数组件中调用,或者是其他hook函数中。下面以官网提供的例子为例:

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

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

使用:

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

这里我们完全不用担心多个函数式组件中复用同一个hook会不会共享state的问题,自定义hook使用的是一种重用状态逻辑的机制。每次使用自定义hook时,其中所有的state和effect都是互相隔离的(相对于所有调用方而言)。

最后,自定义hook需要注意一点:必须以use开头,以便React对自定义hook执行上面3中提到的使用规则的检查。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值