React Hook 的学习(2)

99 阅读2分钟

在上一章,我们粗略的讲解了一个简单的Hook useState 现在我们详细看下常用的Hook的内容。

1. useState

以下就是用得最多的例子,也最容易清楚理解。

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>
  );
}

声明一个count变量,使用useState 组件中有按钮,其点击事件为useState更新它的函数setCount

Hook 是什么?  Hook 是一个特殊的函数,它可以让你“钩入” React 的特性。例如,useState 是允许你在 React 函数组件中添加 state 的 Hook

什么时候我会用 Hook?  如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class。现在你可以在现有的函数组件中使用 Hook。

我们在class组件中在构造函数中设置 this.state 为 { count: 0 } 来初始化 count state 为 0

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {     
        count: 0   
    };  
}

在函数组件中,我们没有 this,所以我们不能分配或读取 this.state。我们直接在组件中调用 useState Hook

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

回到这个方括号

等号左边名字并不是 React API 的部分,你可以自己取名字 我们也可以声明其他的变量

const [name, setName] = useState('ms');

使用useState返回的更新变量的函数进行数据变化。

2. useEffect

Effect 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>
  );
}

数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。不管你知不知道这些操作,或是“副作用”这个名字,应该都在组件中使用过它们

通俗来讲如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMountcomponentDidUpdate 和 componentWillUnmount 这三个函数的组合。

在 React 组件中有两种常见副作用操作:需要清除的和不需要清除的。那就需要进行下一步的学习。

- 不需要清除副作用

有时候,我们只想在 React 更新 DOM 之后运行一些额外的代码。 比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清除的操作。因为我们在执行完这些操作之后,就可以忽略他们了

使用useEffect,你可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它

- 需要清除副作用

我们研究了如何使用不需要清除的副作用,还有一些副作用是需要清除的。例如订阅外部数据源。这种情况下,清除工作是非常重要的,可以防止引起内存泄露

下面我们根据官方文档的例子来进行class和Hook之间的区别假设有一个ChatAPI我们从外部引入组件。允许我们订阅好友的在线状态。通常会在 componentDidMount 中设置订阅,并在 componentWillUnmount 中清除它

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';
  }
}

使用useEffectHook

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);    
      return function cleanup() { //清除机制
           ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
     }; 
 });
 
  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

effect具有可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。

简单用法就是这样。

但是有问题了,useEffect 会在每次渲染后都执行吗?  是的,默认情况下,它在第一次渲染之后and每次更新之后都会执行。你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。

- 使用 Effect 的提示

useState一样,两个无相关联的逻辑可以使用多个useEffectHook

useEffect(() => {   
    document.title = `You clicked ${count} times`;
});

useEffect(() => {   
      function handleStatusChange(status) {  
          setIsOnline(status.isOnline);   
      }   
      ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);    
      return function cleanup() { //清除机制
           ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
     }; 
 });

Hook 允许我们按照代码的用途分离他们,  而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的每一个 effect

  • 通过跳过 Effect 进行性能优化

根据上述React 将按照 effect 声明的顺序依次调用组件中的每一个effect。比如组件重新渲染时候count没有发生了改变,但是useEffect按照顺序下来还是会调用。我们可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可

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

基本用法,知识介绍到这里,还有不足之处。还是请各位观看官方文档