【译】什么是React Hooks

原文:
What are React Hooks?

作者:Robin Wieruch

译者:博轩

《【译】什么是React Hooks》

React Hooks2018年10月的React Conf 中引入,作为在 React 函数组件中运用状况和生命周期的一种要领。虽然函数组件之前被称为 无状况组件(FSC) ,然则 React Hooks 的涌现,使得这些函数组件可以运用状况。因而,如今许多人将它们视为功用组件。

在这篇文章中,我会诠释这些 Hooks 背地的结果,React 会发作什么改变,为何我们不应当惊愕,以及如安在函数式组件中运用罕见的 React Hooks,比方 state生命周期

译注:长文预警 🤓

为何运用 React Hooks

React Hooks 是由 React 团队发现的,用于在函数组件中引入状况治理生命周期要领。假如我们愿望一个 React 函数组件可以具有 状况治理生命周期要领,我不须要再去将一个 React 函数组件重组成一个 React 类组件。React Hooks 让我们可以仅运用函数组件就可以完成一个 React 运用

不必要的组件重构

之前,只要 React 类组件可以运用 当地状况治理生命周期要领 。后者关于在 React 类组件中引入副作用(如监听DOM事宜,异步加载数据)至关主要。

import React from 'react';

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

export default Counter;

只要在您不须要状况生命周期要领时,才会斟酌运用 React 无状况组件(FSC)。而且因为 React 函数组件更轻巧(更文雅),人们已运用了大批的函数组件。每次,当这些 函数组件 须要状况生命周期要领时,都须要将 React 函数组件晋级成 React 类组件(反之亦然)。

import React, { useState } from 'react';

// how to use the state hook in a React function component
function Counter() {
  const [count, setCount] = useState(0);

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

export default Counter;

有了 Hooks ,就没有必要举行这类重构。状况生命周期要领React 函数组件中变得可用。这也让 无状况组件功用组件 的重塑变得切实可行。

副作用逻辑

React 类组件中,副作用主要在生命周期要领中引入 (比方 componentDidMountcomponentDidUpdatecomponentWillUnmount) 。副作用多是React 中猎取数据,或 Browser API 举行交互 。一般这些副作用会伴随着设置和清算阶段。比方,假如您忘记了去删除监听器,就会碰到一些 React 机能题目

// side-effects in a React class component
class MyComponent extends Component {
  // setup phase
  componentDidMount() {
    // add listener for feature 1
    // add listener for feature 2
  }

  // clean up phase
  componentWillUnmount() {
    // remove listener for feature 1
    // remove listener for feature 2
  }

  ...
}

// side-effects in React function component with React Hooks
function MyComponent() {
  useEffect(() => {
    // add listener for feature 1 (setup)
    // return function to remove listener for feature 1 (clean up)
  });

  useEffect(() => {
    // add listener for feature 2 (setup)
    // return function to remove listener for feature 2 (clean up)
  });

  ...
}

如今,假如您在 React 类组件的生命周期要领中引入多个副作用,统统副作用将按生命周期要领分组,而不会按副作用的功用来分组。这就是 React Hooks 带来的改变,将每一个副作用经由过程一个钩子函数举行封装,而每一个钩子函数都邑处置惩罚各自的副作用,并供应这个副作用的设置和清算。稍后您将在本篇教程中看到如安在 React Hook 中增加和删除监听器来完成这一点。

React 笼统地狱

React 中可以经由过程 高阶组件Render Props 来完成笼统和可重用性。另有 React Context及其Provider和消费者组件 所供应的另一个条理的笼统。React 中的统统这些高等形式都运用了所谓的包装组件。关于正在建立更大的 React 运用顺序的开辟人员来讲,以下组件的完成应当并不生疏。

import { compose } from 'recompose';
import { withRouter } from 'react-router-dom';

function App({ history, state, dispatch }) {
  return (
    <ThemeContext.Consumer>
      {theme =>
        <Content theme={theme}>
          ...
        </Content>
      }
    </ThemeContext.Consumer>
  );
}

export default compose(
  withRouter,
  withReducer(reducer, initialState)
)(App);

Sophie Alpert 将之称为 React 中的 “包装地狱” 。您不仅可以在组件完成时看到它,还可以在浏览器中搜检组件时看到它。因为运用 Render Props 组件(包括 React's Context 供应的消费者组件)和高阶组件,很轻易产生了几十个包装组件。因为统统笼统逻辑都被其他 React 组件所隐蔽,我们的运用变成了一棵没有可读性的组件树🌲。而那些可见的组件也很难在浏览器的 DOM 中举行跟踪。那末,假如这些笼统的逻辑在函数组件中被一些封装好的副作用所替代,这些分外的组件将不再须要。

function App() {
  const theme = useTheme();
  const history = useRouter();
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <Content theme={theme}>
      ...
    </Content>
  );
}

export default App;

这就是 React Hooks 的魅力地点。统统副作用都可以直接在组件中运用,营业组件为了运用这些副作用,也不须要再引入其他组件所为容器。容器组件消逝,逻辑只存在于作为函数的 React Hooks 中。

Andrew Clark 已在他的鹅妹子嘤高阶组件库:recompose 中宣布了一篇关于赞同 React Hooks声明

杂沓的 JavaScript

JavaScript 很好地夹杂了两个天下:面向对象编程(OOP)函数式编程(FP)React 将许多的开辟者带到了这两个天下。一方面,React (和 Redux )向人们引见了 函数编程(FP) 的功用组合,一些函数式编程的通用编程观点(比方高阶函数,JavaScript 内置要领,如 mapreducefilter ),以及一些函数式编程的术语,如 不变性副作用React 本身没有真正引见这些东西,因为它们是言语或编程范式的特征,但它们在 React 中被大批运用,这使得每一个 React 开辟人员都邑自动成为一个更好的 JavaScript 开辟人员

另一方面,在 React 中,可以运用 JavaScript 类作为定义 React 组件的一种要领。类只是声明,而组件的现实用法是它的实例。它会建立一个类实例,而类实例的 this 对象可以用于挪用类的要领(比方 setStateforceUpdate ,以及其他自定义类要领)。然则,关于不是来自 OOP 背景的 React 初学者来讲,进修曲线会更峻峭。这就是为何类绑定,this 对象和继承可以令人困惑的缘由。关于初学者来讲,这一直是 React 最令人困惑的事变,我的 React书 中也只要几章在讲这方面的学问。

// I THOUGHT WE ARE USING A CLASS. WHY IS IT EXTENDING FROM SOMETHING?
class Counter extends Component {
  // WAIT ... THIS WORKS???
  state = { value: 0 };

  // I THOUGH IT'S THIS WAY, BUT WHY DO I NEED PROPS HERE?
  // constructor(props) {
  //  SUPER???
  //  super(props);
  //
  //  this.state = {
  //    value: 0,
  //  };
  // }

  // WHY DO I HAVE TO USE AN ARROW FUNCTION???
  onIncrement = () => {
    this.setState(state => ({
      value: state.value + 1
    }));
  };

  // SHOULDN'T IT BE this.onDecrement = this.onDecrement.bind(this); in the constructor???
  // WHAT'S this.onDecrement = this.onDecrement.bind(this); DOING ANYWAY?
  onDecrement = () => {
    this.setState(state => ({
      value: state.value - 1
    }));
  };

  render() {
    return (
      <div>
        {this.state.value}

        {/* WHY IS EVERYTHING AVAILABLE ON "THIS"??? */}
        <button onClick={this.onIncrement}>+</button>
        <button onClick={this.onDecrement}>-</button>
      </div>
    )
  }
}

如今,有许多人在争辩 React 不应当移除 JavaScript classes, 只管他们不明白这些类的观点。毕竟,这些观点都来自于言语本身。然则,引入 Hooks API 的假定之一就是让初学者在第一次编写 React 运用时可以不必运用类组件,从而供应越发腻滑的进修曲线。

React Hooks 会发作什么变化?

每次引入新功用时,人们都邑关注它。一些人对这一变化觉得欣喜若狂,而别的一些人对这一变化觉得担心。我据说 React Hooks 最罕见的题目是:

  • 统统都变了!莫名的觉得惊愕……
  • ReactAngular 一样变得痴肥!
  • Hooks 没有用,classes 很好
  • 这是魔法!

让我在这里处理这些题目:

统统都在变化?

React Hooks 将改变我们将来编写 React 运用顺序的体式格局。然则,现在没有任何变化。您依然可以运用当地状况和生命周期要领编写类组件,并布置高等形式,比方高阶组件或 Render Props 组件。没有人会把这些学问从你身旁带走。相识我怎样将统统开源项目从旧版本晋级到 React 16.6 。这些项目都没有题目。他们正在运用 HOCRender Props ,以至是较为陈旧的 context API(假如我错了,请改正我)。这些年来我所学到的统统依然有用。React 团队确保 React 坚持向后兼容。它与 React 16.7 坚持雷同。

《【译】什么是React Hooks》

ReactAngular 一样变得痴肥

React 作为一个第三方库,其 API 总是会给人一种短小精干的觉得 。如今是如许,将来也是云云。然则,斟酌到几年前基于组件所构建的运用,在晋级的时刻不被其他越发先进的库所庖代, React 引入了有利于旧 API 的变动。假如 React 是9012年宣布的,或许他的功用只会保存函数组件和 Hooks。但 React 几年前就已宣布,它在宣布新的特征的同时,还须要兼容之前的版本。可以会在几年弃用类组件和其生命周期要领,转而运用 React 函数组件和 Hooks,但现在,React 团队仍将 React 类组件保存在了他们的东西库中。毕竟, React 团队愿望应用新的特征 Hooks 来陪同 React 博得一场马拉松,而不是一场短跑。明显,React HooksReact 增加了另一个 API,但它有利于在将来简化 React 的新 API。我喜好这类改变,而不是具有 “React 2”,让统统都差别。

译注:这是在吐槽
Angular 嘛?😂

Hooks 没有用,classes 很好 ?

设想一下,你将从零最先进修 React ,你会被引见给 React with Hooks 。或许 create-react-app 不会以 React 类组件最先,而是运用 React 函数组件。您须要相识组件的统统内容都是 React Hooks 。它们可以治理状况和副作用,因而您只须要晓得 state hookeffect hook。这是 React 类组件之前为包括的统统。React 初学者进修 React 会更简朴,而不须要 JavaScript 类(继承,thisbindingssuper,…)带来的统统其他开支。设想一下 React Hooks 作为一种编写 React 组件的新要领 – 这是一种全新的头脑体式格局。我本身是一个持怀疑态度的人,然则一旦我用 React Hooks 写了几个更简朴的场景,我确信这是最简朴的 React 写作体式格局,一样也进修 React 最简朴的体式格局。作为一个做了许多 React workshops 的人,我认为它消除了那些令 React 初学者所懊丧的 classes

这是魔法?

尽人皆知,React 是用 JavaScript 来完成的。当有人问我:“我为何要进修 React ?”时,最好的诠释就是:编写 React 运用顺序会让你成为一个更好的 JavaScript 开辟人员 。不管将来是不是会运用新的库,每一个人都可以在运用 React 的过程当中,考验他们的 JavaScript 妙技和一些通用的编程技能。这是 Redux 常常做的事变,一样也是 React 做的事变:盛行,然则没有魔力,它只是一般的 JavaScript 。如今 React Hooks 涌现了,在之前常常运用的纯函数组件中引入了一些有状况的东西,同时还引入了一些不轻易让人吸收的划定规矩,这使得许多人都不邃晓底层发作了什么。然则如许斟酌一下:在React 中的一个函数组件不仅仅是一个函数。您依然必需将 React 作为库导入到源代码文件中。它对你的函数做了一些事变,使得函数在 React 的天下中变成了函数组件。此函数组件的完成一直是隐式的。如安在将 React Hooks 引入之前运用函数完成的函数组件呢?人们也吸收了它,纵然它有点像是一个魔法。如今,唯一改变的东西(或许它之前已是如许)是这些函数组件带有一个分外的隐蔽对象,可以跟踪的 Hooks。援用 Dan Abramov他的关于 Hooks 文章的话:“或许你想晓得 React 在那里为 Hooks 坚持状况。答案是它保存在 React 为类坚持状况的完全雷同的位置。不管你怎样定义你的组件,React 都邑在内部保护一个更新行列”

末了,试一下这类思索体式格局

基于组件的处理方案(如 AngularVueReact)正在推进每一个版本的 Web 开辟的界线。它们建立在二十多年前发现的手艺之上。他们的涌现是为了让2018年的开辟者越发轻松,而不是1998。他们猖獗地优化本身以满足如今和如今的需求。我们正在运用组件而不是 HTML模板 构建 Web 运用顺序。虽然还没有完成,但我设想一个将来,我们坐在一同,为浏览器发现一个基于组件的规范。AngularVueReact 只是这一活动的前锋。

鄙人文中,我想经由过程示例深切引见一些受欢迎的 React Hooks ,以帮助您加快相识这一变化。统统示例都可以在此GitHub存储库中找到。

React useState Hook

上文中,您已在一个典范的计数器示例的代码片断中看到过 useState Hook。它用于治理函数组件中的当地状况。让我们在一个更细致的例子中运用 Hook,我们将治理一系列项目。在我的另一篇文章中,您可以相识有关在React中将数组作为状况举行治理的更多信息,但这次我们运用 React Hook 举行操纵。让我们最先吧:

import React, { useState } from 'react';

const INITIAL_LIST = [
  {
    id: '0',
    title: 'React with RxJS for State Management Tutorial',
    url:
      'https://www.robinwieruch.de/react-rxjs-state-management-tutorial/',
  },
  {
    id: '1',
    title: 'A complete React with Apollo and GraphQL Tutorial',
    url: 'https://www.robinwieruch.de/react-graphql-apollo-tutorial',
  },
];

function App() {
  const [list, setList] = useState(INITIAL_LIST);

  return (
    <ul>
      {list.map(item => (
        <li key={item.id}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;

useState Hook 吸收一个初始状况作为参数,并经由过程运用数组解构返回两个可以定名的变量,您可以为它们取须要的名字。第一个变量是现实状况,而第二个变量是一个可以设置并返回新状况的函数。

这个示例的下一步是从列表中删除子元素。为了完成它,我们为列表中的每一个子元素都设置了一个可单击按钮。单击处置惩罚顺序会运用一个内联函数完成,因为稍后会在内联函数中运用 list 以及 setList 。 因而,您不须要将这些变量通报给处置惩罚顺序,因为它们已可以从组件的外部作用域中取得。

function App() {
  const [list, setList] = useState(INITIAL_LIST);

  function onRemoveItem() {
    // remove item from "list"
    // set the new list in state with "setList"
  }

  return (
    <ul>
      {list.map(item => (
        <li key={item.id}>
          <a href={item.url}>{item.title}</a>
          <button type="button" onClick={onRemoveItem}>
            Remove
          </button>
        </li>
      ))}
    </ul>
  );
}

因为一些缘由,我们须要从列表中删除的子元素。运用高阶函数,我们可以将子元素的标识符通报给处置惩罚函数。不然,我们将没法从列表中删除的这些子元素。

function App() {
  const [list, setList] = useState(INITIAL_LIST);

  function onRemoveItem(id) {
    // remove item from "list"
    // set the new list in state with "setList"
  }

  return (
    <ul>
      {list.map(item => (
        <li key={item.id}>
          <a href={item.url}>{item.title}</a>
          <button type="button" onClick={() => onRemoveItem(item.id)}>
            Remove
          </button>
        </li>
      ))}
    </ul>
  );
}

末了,运用数组的内置要领过滤列表,删除包括标识符的子元素。它返回一个新列表,用于设置列表的新状况。

function App() {
  const [list, setList] = useState(INITIAL_LIST);

  function onRemoveItem(id) {
    const newList = list.filter(item => item.id !== id);
    setList(newList);
  }

  return (
    <ul>
      {list.map(item => (
        <li key={item.id}>
          <a href={item.url}>{item.title}</a>
          <button type="button" onClick={() => onRemoveItem(item.id)}>
            Remove
          </button>
        </li>
      ))}
    </ul>
  );
}

此时应当已完成了。您可以依据通报给处置惩罚顺序的标识符从列表中删除子元素。然后,处置惩罚函数过滤列表并运用 setList 函数设置列表的新状况。

useState Hook 为您供应了在函数组件中治理状况所需的统统:初始状况,最新状况和状况更新功用。其他统统都是 JavaScript 。另外,您不须要像之前一样在类组件中运用浅兼并来坚持 state 的更新。相反,您运用 useState 封装一个域(比方列表),但假如您须要另一个状况(比方计数器),则只需运用另一个 useState 封装此域。您可以在 React 的文档中浏览有关 useState Hook 的更多信息

React useEffect Hook

让我们转到下一个名为 useEffectHook。如前所述,功用组件应当可以运用 Hook 治理状况和副作用。上面我们已运用 useState Hook 展现了治理状况。如今将 useEffect Hook 用于副作用,这些副作用一般用于与 Browser / DOM API 或外部 API(如数据猎取)的交互。让我们看一下怎样经由过程完成一个简朴的秒表,将 useEffect HookBrowser API 相结合。您可以在GitHub 堆栈中检察运用 React 类组件须要怎样完成。

import React, { useState } from 'react';

function App() {
  const [isOn, setIsOn] = useState(false);

  return (
    <div>
      {!isOn && (
        <button type="button" onClick={() => setIsOn(true)}>
          Start
        </button>
      )}

      {isOn && (
        <button type="button" onClick={() => setIsOn(false)}>
          Stop
        </button>
      )}
    </div>
  );
}

export default App;

如今还没有秒表。但如今至少有前提衬着显现 “最先”“住手” 按钮。而且由 useState hook 来治理布尔标志的状况。

接下来让我们到场副作用:用 useEffect 来注册一个 interval 定时器。定时器函数每秒会向您的浏览器控制台发出一条纪录。

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

function App() {
  const [isOn, setIsOn] = useState(false);

  useEffect(() => {
    setInterval(() => console.log('tick'), 1000);
  });

  return (
    <div>
      {!isOn && (
        <button type="button" onClick={() => setIsOn(true)}>
          Start
        </button>
      )}

      {isOn && (
        <button type="button" onClick={() => setIsOn(false)}>
          Stop
        </button>
      )}
    </div>
  );
}

export default App;

为了在组件卸载的时刻移除定时器(以及每次衬着更新以后),您可以在 useEffect 中返回一个函数,以便清算任何内容。关于上面的例子,当组件不再存在时,不应当留下任何内存走漏。

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

function App() {
  const [isOn, setIsOn] = useState(false);

  useEffect(() => {
    const interval = setInterval(() => console.log('tick'), 1000);

    return () => clearInterval(interval);
  });

  ...
}

export default App;

如今,您须要在装置组件时设置副作用,并在卸载组件时消灭副作用。假如您须要纪录函数的挪用次数,您会看到每次组件状况发作变化时它都邑设置一个新的定时器(比方,单击“最先”/“住手”按钮)。

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

function App() {
  const [isOn, setIsOn] = useState(false);

  useEffect(() => {
    console.log('effect runs');
    const interval = setInterval(() => console.log('tick'), 1000);

    return () => clearInterval(interval);
  });

  ...
}

export default App;

为了仅在组件的 mountunmount 时相应 ,可以将空数组作为第二个参数通报给它。

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

function App() {
  const [isOn, setIsOn] = useState(false);

  useEffect(() => {
    const interval = setInterval(() => console.log('tick'), 1000);

    return () => clearInterval(interval);
  }, []);

  ...
}

export default App;

然则,因为在每次衬着以后都邑消灭定时器,我们也须要在我们的营业周期中设置定时器。我们也可以通知 effect 仅在 isOn 变量发作变化时运转。仅当数组中的一个变量发作更改时,effect 才会在更新周期中运转。假如将数组坚持为空, effect 将仅在 mountunmount 时运转,因为没有要搜检的变量是不是再次运转副作用。

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

function App() {
  const [isOn, setIsOn] = useState(false);

  useEffect(() => {
    const interval = setInterval(() => console.log('tick'), 1000);

    return () => clearInterval(interval);
  }, [isOn]);

  ...
}

export default App;

如今,不管 isOn 布尔值是 true 照样 false ,定时器都在运转。接下来,我们愿望定时器仅在秒表启动的时刻运转。

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

function App() {
  const [isOn, setIsOn] = useState(false);

  useEffect(() => {
    let interval;

    if (isOn) {
      interval = setInterval(() => console.log('tick'), 1000);
    }

    return () => clearInterval(interval);
  }, [isOn]);

  ...
}

export default App;

如今在功用组件中引入另一个状况来跟踪秒表的计时器。它用于更新计时器,但仅在秒表被激活时运用。

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

function App() {
  const [isOn, setIsOn] = useState(false);
  const [timer, setTimer] = useState(0);

  useEffect(() => {
    let interval;

    if (isOn) {
      interval = setInterval(
        () => setTimer(timer + 1),
        1000,
      );
    }

    return () => clearInterval(interval);
  }, [isOn]);

  return (
    <div>
      {timer}

      {!isOn && (
        <button type="button" onClick={() => setIsOn(true)}>
          Start
        </button>
      )}

      {isOn && (
        <button type="button" onClick={() => setIsOn(false)}>
          Stop
        </button>
      )}
    </div>
  );
}

export default App;

代码中依然存在一个毛病。当定时器运转时,它会将 timer 每秒加 1 。然则,它一直依赖于计时器的初始状况在累加。只要当 inOn 布尔标志改变时,状况才会一般显现。为了在计时器在运转时,一直吸收最新的状况,您可以运用函数替代替代状况,是的每次更新的时刻都可以拿到最新的状况。

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

function App() {
  const [isOn, setIsOn] = useState(false);
  const [timer, setTimer] = useState(0);

  useEffect(() => {
    let interval;

    if (isOn) {
      interval = setInterval(
        () => setTimer(timer => timer + 1),
        1000,
      );
    }

    return () => clearInterval(interval);
  }, [isOn]);

  ...
}

export default App;

另一种要领是,在计时器改变时,运转 effect。然后 effect 将收到最新的计时器状况。

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

function App() {
  const [isOn, setIsOn] = useState(false);
  const [timer, setTimer] = useState(0);

  useEffect(() => {
    let interval;

    if (isOn) {
      interval = setInterval(
        () => setTimer(timer + 1),
        1000,
      );
    }

    return () => clearInterval(interval);
  }, [isOn, timer]);

  ...
}

export default App;

这是运用浏览器 API 完成的秒表结果,假如您想继承,您也可以经由过程供应 “重置” 按钮来扩大现例。

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

function App() {
  const [isOn, setIsOn] = useState(false);
  const [timer, setTimer] = useState(0);

  useEffect(() => {
    let interval;

    if (isOn) {
      interval = setInterval(
        () => setTimer(timer => timer + 1),
        1000,
      );
    }

    return () => clearInterval(interval);
  }, [isOn]);

  const onReset = () => {
    setIsOn(false);
    setTimer(0);
  };

  return (
    <div>
      {timer}

      {!isOn && (
        <button type="button" onClick={() => setIsOn(true)}>
          Start
        </button>
      )}

      {isOn && (
        <button type="button" onClick={() => setIsOn(false)}>
          Stop
        </button>
      )}

      <button type="button" disabled={timer === 0} onClick={onReset}>
        Reset
      </button>
    </div>
  );
}

export default App;

That’s it. 运用 useEffect hook 来治理 React 函数组件的副作用,比方 Browser / DOM API 或其他第三方 API 的交互(比方数据猎取)。您可以在 React 官方文档 useEffect hook 部份 猎取更多信息。

自定义 React Hooks

末了一样主要的是,在您相识了两个最经常使用的在函数组件中引入状况和副作用的 Hooks 以后,我还想通知您末了一件事:自定义 Hooks 。没错,您可以完成本身的自定义 React Hooks,在您的运用顺序中或供应给其他人中运用。让我们看看接下来的示例:一个可以检测您的装备是在线照样离线的运用顺序是怎样事情的。

import React, { useState } from 'react';

function App() {
  const [isOffline, setIsOffline] = useState(false);

  if (isOffline) {
    return <div>Sorry, you are offline ...</div>;
  }

  return <div>You are online!</div>;
}

export default App;

接下来,为副作用引入 useEffect hook。在这类情况下,effect 会增加和删除搜检装备是联机照样脱机的监听器。两个监听器在 mount 时只设置一次,在 unmount 时清算一次(空数组作为第二个参数)。每当挪用个中一个监听器时,它就会设置 isOffline 布尔值的状况。

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

function App() {
  const [isOffline, setIsOffline] = useState(false);

  function onOffline() {
    setIsOffline(true);
  }

  function onOnline() {
    setIsOffline(false);
  }

  useEffect(() => {
    window.addEventListener('offline', onOffline);
    window.addEventListener('online', onOnline);

    return () => {
      window.removeEventListener('offline', onOffline);
      window.removeEventListener('online', onOnline);
    };
  }, []);

  if (isOffline) {
    return <div>Sorry, you are offline ...</div>;
  }

  return <div>You are online!</div>;
}

export default App;

如今,让我们将这些功用封装在一个 Effect 中。这是一个很棒的功用,我们愿望其他处所也可以重用。这就是为何我们可以提取功用,作为一个自定义 Hook, 它遵照与其他 Hook 雷同的定名商定。

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

function useOffline() {
  const [isOffline, setIsOffline] = useState(false);

  function onOffline() {
    setIsOffline(true);
  }

  function onOnline() {
    setIsOffline(false);
  }

  useEffect(() => {
    window.addEventListener('offline', onOffline);
    window.addEventListener('online', onOnline);

    return () => {
      window.removeEventListener('offline', onOffline);
      window.removeEventListener('online', onOnline);
    };
  }, []);

  return isOffline;
}

function App() {
  const isOffline = useOffline();

  if (isOffline) {
    return <div>Sorry, you are offline ...</div>;
  }

  return <div>You are online!</div>;
}

export default App;

将自定义 Hook 作为函数提取出来并非唯一的事变。您还必需将 isOffline 状况从自定义 Hook 中返回,以便在运用顺序中向用户显现是不是离线的音讯。不然,它应当显现一般的运用顺序。这就是自定义 Hook ,它可以检测您是在线照样离线。您可以在 React 的文档中浏览有关自定义 Hook 的更多信息。

可重用的 React Hooks 最棒的处所,是它有可以生长自定义 React Hooks 的生态系统,我们可以从 npm 为任何 React 运用顺序装置 Hooks。而且不仅仅是 React 运用顺序。Vue的作者 Evan You (尤雨溪) 也被 Hooks 迷住了!。或许我们会看到两个生态系统之间的桥梁,可以在 VueReact 之间同享 Hooks

假如您想深切相识 state hookeffect hook ,可以检察以下 React Hooks 教程:

检察 React 文档中关于钩子的官方FAQ划定规矩,以相识有关其细粒度行动的更多信息。另外,您还可以检察统统官方供应的React Hooks

关于依然关注 React Hooks 的每一个人:给本身一个时机。运用状况和副作用完成几个 React 函数组件。我们必需本身去明白它们是怎样事情的,感觉这类编程体验的晋级。我必需说运用它们觉得异常棒。

译注:给我一个时机,重学 React ~

本文已联络原文作者,并受权翻译,转载请保存原文链接

    原文作者:joking_zhang
    原文地址: https://segmentfault.com/a/1190000019227865
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞