React Hooks的最佳实践

React Hooks的最佳实践

时间:2020-7-13 作者:gykj

React Hooks是React 16.8中的新增功能,使您无需编写class组件即可使用状态和其他React功能。换句话说,挂钩是使您可以“挂钩”功能组件中的React状态和生命周期功能的函数。(它们在class组件内部不起作用。)

React提供了一些内置的Hooks useState。您还可以创建自己的Hook,以在不同组件之间重用有状态行为。下面的示例显示一个计数器,该计数器的状态使用useState()挂钩进行管理。每次您单击按钮时,我们都会利用setCount()来更新countby 的值1

见笔阵营与反钩例如Adeneye阿维奥东大卫

本示例呈现一个值为的计数器0。当您单击按钮时,它会将值增加1。使用定义组件的初始值useState

const [count, setCount] = useState(0)

如您所见,我们将其设置为0。然后onClick()setCount当我们想增加值时,使用该方法进行调用。

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

在发布React Hooks之前,此示例将使用更多的代码行,因为我们不得不使用class组件。

反应钩规则

在深入探讨最佳实践之前,我们需要了解React Hooks的规则,这也是本文介绍的实践的一些基本概念。

React Hook是JavaScript函数,但是使用它们时需要遵循两个规则。

  1. 在顶层调用Hooks ;
  2. 仅从React组件调用Hook

注意这两个规则是在React Hooks中引入的,而不是JavaScript本身的一部分。

让我们更详细地了解这些规则。

顶层呼叫挂钩

不要在循环,条件或嵌套函数中调用Hook。始终在React函数的顶层使用Hook。通过遵循此规则,可以确保每次渲染组件时都以相同的顺序调用Hook。那是什么让反应正确保存多个鱼钩之间的状态useStateuseEffect来电。

让我们制作一个Form具有两个状态的组件:

  • accountName
  • accountDetail

这些状态将具有默认值,我们将利用useEffect钩子将状态持久化到浏览器的本地存储或文档的标题中。

现在,这部分将是可能成功管理其状态,如果它仍然是多次调用之间的相同useStateuseEffect

function Form() {
  // 1. Use the accountName state variable
  const [accountName, setAccountName] = useState('David');

  // 2. Use an effect for persisting the form
  useEffect(function persistForm() {
    localStorage.setItem('formData', accountName);
  });

  // 3. Use the accountDetail state variable
  const [accountDetail, setAccountDetail] = useState('Active');

  // 4. Use an effect for updating the title
  useEffect(function updateStatus() {
    document.title = accountName + ' ' + accountDetail;
  });

  // ...
}

如果我们的Hook的顺序发生变化(在循环或有条件的条件下调用它们的可能),React将很难确定如何保留组件的状态。

// ------------
useState('David')           // 1. Initialize the accountName state variable with 'David'
useEffect(persistForm)     // 2. Add an effect for persisting the form
useState('Active')        // 3. Initialize the accountdetail state variable with 'Active'
useEffect(updateStatus)     // 4. Add an effect for updating the status

// -------------
// Second render
// -------------
useState('David')           // 1. Read the accountName state variable (argument is ignored)
useEffect(persistForm)     // 2. Replace the effect for persisting the form
useState('Active')        // 3. Read the accountDetail state variable (argument is ignored)
useEffect(updateStatus)     // 4. Replace the effect for updating the status

// ...

这就是React调用我们的钩子的顺序。由于顺序保持不变,因此将能够保留组件的状态。但是,如果我们将Hook呼叫置于条件内会怎样?

// 🔴 We're breaking the first rule by using a Hook in a condition
  if (accountName !== '') {
    useEffect(function persistForm() {
      localStorage.setItem('formData', accountName);
    });
  }

accountName !== ''条件是true在第一渲染,所以我们运行此挂钩。但是,在下一次渲染时,用户可能会清除表单,从而形成条件false。现在,我们在渲染过程中跳过了该Hook,Hook调用的顺序变得不同:

useState('David')           // 1. Read the accountName state variable (argument is ignored)
// useEffect(persistForm)  // 🔴 This Hook was skipped!
useState('Active')        // 🔴 2 (but was 3). Fail to read the accountDetails state variable
useEffect(updateStatus)     // 🔴 3 (but was 4). Fail to replace the effect

React不知道第二次useStateHook调用返回什么。React期望此组件中的第二个Hook调用与该persistForm效果相对应,就像在上一个渲染期间一样,但是不再。从那时起,Hook我们跳过的呼叫之后的下一个呼叫也将移动一个,从而导致错误。

这就是为什么必须在我们组件的顶层调用Hook的原因。如果要有条件地运行效果,可以将该条件放入挂钩中。

注意查阅React Hook文档以了解有关此主题的更多信息。

仅从React组件调用挂钩

不要从常规的JavaScript函数中调用Hook。相反,您可以从React函数组件中调用Hook。让我们看看下面的JavaScript函数和React组件之间的区别:

JavaScript功能
import { useState } = "react";

function toCelsius(fahrenheit) {
  const [name, setName] = useState("David");
  return (5/9) * (fahrenheit-32);
}
document.getElementById("demo").innerHTML = toCelsius;

在这里,我们useState从React包中导入钩子,然后声明我们的函数。但这是无效的,因为它不是React组件。

反应功能

import React, { useState} from "react";
import ReactDOM from "react-dom";

function Account(props) {
  const [name, setName] = useState("David");
  return <p>Hello, {name}! The price is <b>{props.total}</b> and the total amount is <b>{props.amount}</b></p>
}
ReactDom.render(
  <Account total={20} amount={5000} />,
  document.getElementById('root')
);

即使两者的外观相似,但是当我们将React导入文件时,后者成为一个组件。这就是使我们能够在内部使用诸如JSX和React挂钩之类的东西的原因。

如果您碰巧导入了首选挂钩而不导入React(这使其成为常规函数),则您将无法使用已导入的挂钩,因为挂钩只能在React组件中访问。

从自定义挂钩呼叫挂钩

自定义挂钩是一个JavaScript函数,其名称以开头,use并且可以调用其他挂钩。例如,useUserName在自定义钩子下面使用,该钩子调用useStateuseEffect钩子。它从API提取数据,循环访问数据,并setIsPresent()在API数据中包含接收到的特定用户名时调用。

export default function useUserName(userName) {
  const [isPresent, setIsPresent] = useState(false);
  
  useEffect(() => {
    const data = MockedApi.fetchData();
    data.then((res) => {
      res.forEach((e) => {
        if (e.name === userName) {
          setIsPresent(true);
        }
     });
    });
  });
    
  return isPresent;
}

然后,我们可以继续在应用程序中其他需要的地方重用此钩子的功能。在这样的地方,除非需要,否则我们不必打电话useStateuseEffect再打电话。

通过遵循此规则,可以确保组件中的所有状态逻辑从其源代码中都清晰可见。

ESLINT插件

名为ESLint的插件会eslint-plugin-react-hooks强制执行上述规则。在项目上执行规则时,这非常方便。我建议您在处理项目时使用此插件,尤其是与他人一起使用时。您可以尝试将此插件添加到您的项目中:

// Your ESLint configuration
{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
    "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
  }
}

默认情况下,此插件包含在Create React App中。因此,如果使用Create-React-App引导React应用程序,则无需添加它。

钩中思考

class在深入探讨一些Hooks最佳实践之前,让我们简要介绍一下组件和功能组件(使用Hooks)。

在React中定义组件的最简单方法是编写一个返回React元素的JavaScript函数:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

Welcome组件接受props哪个对象包含数据并返回React元素。然后,我们可以将该组件导入并渲染到另一个组件中。

class组件使用一种称为封装的编程方法,该方法基本上意味着与类组件相关的所有内容都将存在于其中。生命周期方法(constructorscomponentDidMount()render,等等)给部件可预测的结构。

封装是OOP(的基本原则之一ö bject- ö riented P在AGC)。它指的是在对数据进行操作的方法内捆绑数据,并用于隐藏类内结构化数据对象的值或状态-防止未经授权的方直接访问它们。

使用Hooks,组件的组成从生命周期Hooks的组合更改为最终带有某些渲染的功能。

功能组件

下面的示例显示了如何在功能组件中使用自定义的Hook(不显示主体是什么)。但是,它可以做什么或可以做什么不受限制。它可能是实例化状态变量,使用上下文,将组件订阅各种副作用-如果使用自定义钩子,则可能是上述所有操作!

function {
  useHook{...};
  useHook{...};
  useHook{...};
  return (
    ...
  );
}

类组件

class组件要求您从延伸React.Component,并创建一个render返回一个阵营元件的功能。这需要更多代码,但也会给您带来一些好处。

class {
  constructor(props) {...}
  componentDidMount() {...}
  componentWillUnmount() {...}
  render() {...}
}

通过在React中使用功能组件可以获得一些好处:

  1. 分离容器组件和演示组件将变得更加容易,因为如果您无权访问组件,则需要更多考虑组件的状态setState()
  2. 功能组件更容易阅读和测试,因为它们是普通的JavaScript函数,没有状态或生命周期挂钩。
  3. 您最终得到的代码更少。
  4. React团队提到,未来的React版本中功能组件的性能可能会提高。

这是使用React Hooks时的第一个最佳实践。

挂钩最佳做法

1.简化钩子

使React Hooks保持简单将使您有能力有效地控制和操纵组件在整个生命周期中发生的事情。尽量避免编写自定义的Hook;您可以内联一个useState()useEffect()而不是创建自己的钩子。

如果您发现自己使用了功能上相关的一堆自定义挂钩,则可以创建一个自定义挂钩来充当这些包装的包装。让我们看一下下面带有钩子的两个不同的功能组件。

功能组件v1
function {
  useHook(...);
  useHook(...);
  useHook(...);
  return(
    <div>...</div>
  );
}
功能组件v2
function {
  useCustomHook(...);
    useHook(...);
    useHook(...);
  return(
    <div>...</div>
  );
}

v2是更好的版本,因为它使钩子保持简单,而其他所有useHooks都相应地内联。这使我们能够创建可在不同组件之间重用的功能,还使我们有更多能力有效地控制和操纵我们的组件。您应该采用v2,而不是采用v1,因为我们的组件中有很多Hooks,而应该使用v2,这将使调试变得容易并且代码清洁。

2.组织和构造你的钩子

React Hooks的优点之一是能够编写更少易读的代码。在某些情况下,量useEffect()useState()仍然可以混淆。当您使组件井井有条时,它将有助于提高可读性,并使组件流保持一致和可预测。如果您的自定义挂钩过于复杂,则始终可以将其分解为子自定义挂钩。将组件的逻辑提取到自定义Hook,以使您的代码可读。

3.使用REACT HOOKS代码段

React Hooks片段是Visual Studio Code的扩展,可以使React Hooks变得更轻松,更快捷。当前,支持五个钩子:

  • useState()
  • useEffect()
  • useContext()
  • useCallback()
  • useMemo()

其他片段也已添加。我已经尝试过使用这些挂钩,这一直是我与它们一起使用时最好的最佳实践之一。

您可以通过两种方式将React Hooks片段添加到您的项目中:

  1. 命令
    启动VS Code快速打开(CtrlP),粘贴ext install ALDuncanson.react-hooks-snippets并按Enter
  2. Extension Marketplace
    启动“ VS Code Extension Marketplace”(CtrlShiftX)并搜索“ React Hook Snippets”。然后,查找“ Alduncanson”图标。

我推荐第一个片段。了解更多有关片段在这里或检查的最新的鱼钩片段在这里

4.考虑钩子规则

努力在使用React Hooks时始终将我们先前学习的Hooks的两个规则考虑在内。

  • 仅在顶层调用您的挂钩。不要在循环,条件或嵌套函数中调用Hook。
  • 始终从React函数组件或自定义Hook调用Hook,不要从常规JavaScript函数调用Hook。

名为ESlint的插件会eslint-plugin-react-hooks强制执行这两个规则,如果您愿意的话,可以将其添加到您的项目中,如我们在“钩子规则”部分中所述。

最佳实践尚未完全解决,因为Hooks仍相对较新。因此,应谨慎采用,以免在任何早期技术中采用。考虑到这一点,Hooks是React的未来之路。

结论

希望您喜欢本教程。我们已经了解了React Hooks的两个最重要的规则,以及如何在Hooks中有效思考。我们以正确有效的方式研究了功能组件和一些最佳实践,以编写Hooks。尽管规则简短,但是在编写规则时使它们成为您的指南针很重要。如果您容易忘记它,可以使用ESLint插件来强制执行它。

我希望您可以在下一个React项目中吸取这里所有的经验教训。祝好运!

版权所有:https://www.eraycloud.com 转载请注明出处