React Hooks

12.22'21

介绍

Hooks 是一类函数,实现在函数组件中使用 React 状态和生命周期(state and lifecycle)。类组件不再是唯一的选择。自定义 hooks 还可以更好地复用状态相关的逻辑。

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.

Change

Thinking in terms of state and synchronization with DOM from lifecycles and time

State Hook

useState,参数为初始化状态,返回状态变量和修改函数。用数组方式接收可以自定义变量和函数名。

import React, { useState } from "react";

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

Effect Hook

side effects(或简称为 effects):是指异步获取数据,订阅数据,直接修改 DOM 等,会影响其他组件且不能在渲染中完成的操作。

useEffect(effect, [dependencies]),参数为回调函数。每次渲染后调用。返回值如果是函数,会在下一次触发 effect 前调用,执行清理相关逻辑。

If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

一个函数组件内可以使用多个useEffect,分布实现didMount, didUpdate等效果。

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return "Loading...";
  }
  return isOnline ? "Online" : "offline";
}

自定义 Hook

在函数中包装原生 hooks。这个函数就是自定义 hook。

自定义 hook 可以返回有状态的变量,变化后会触发组件刷新(reactive)。

自定义 Hook 的方案不用添加额外组件。复用状态逻辑,传统方案有:高阶组件render 属性等。

import { useState, useEffect } from "react";

// 定义 hook
function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener("resize", handleResize);
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  });
  return width;
}

// 使用 hook
function MyResponsiveComponent() {
  const width = useWindowWidth(); // Our custom hook
  return <p>Window with is {width}</p>;
}

useRef

useRef返回引用, 属性.current会指向 DOM 节点。

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

Hooks 的实现和使用规则

实现原理是数组,闭包?

Making Sense of React Hooks

React hooks: not magic, just arrays

  • 仅在函数组件 最外层调用,或者自定义 Hooks内调用

  • 不要在循环、条件和嵌套函数内调用

Tips:5 Tips to Help You Avoid React Hooks Pitfalls

为什么引入 Hooks

Class Component 过去存在的问题:

  • 生命周期方法容易导致,不同逻辑的代码在同一个方法,同一逻辑的代码在不同方法内

  • 很难共享状态相关逻辑

  • 很难理解复杂的组件,过度耦合

    Event listeners 会涉及 componentDidMountcomponentWillUnmount

  • 很难理解this 和函数上下文

  • 很难调试和优化

    HOC 或者 renderProps 在调试时会出现多余的组件结构

Function Component 的问题:

  • 无法利用shouldComponentUpdate进行优化,但可以使用React.memo。类似于React.PureComponent

  • useEffect 不如 Class 生命周期那么全面和直观。主要用于同步副作用和状态。

  • 学习曲线

React Function Components with hooks vs Class Components

何时使用

建议优先使用函数组件加 hooks,需要较多内部状态和生命周期时考虑使用 Class。

应用举例:共享状态

state-management-with-react-hooks-no-redux-or-context-api

📖